📄 fat.c
字号:
// If the required FAT sector isn't already in memory, then read it...
ASSERT((lFATSector != 0),("FAT sector cannot be zero!"));
if (lFATSector != m_lCurFATSector) {
if (!ReadSector(lFATSector, (PXBYTE) &m_awCurFAT)) {
DBGOUT(("NextCluster: Error reading FAT sector %ld\n", lFATSector));
return EOF_CLUSTER;
}
//DBGOUT(("NextCluster: Read FAT sector %ld ...\n", lFATSector));
m_lCurFATSector = lFATSector;
}
// The rest is easy!
return WSWAP(m_awCurFAT[bFATOffset]);
}
//++
// This routine will set up the current file data (m_?CurFile???) to the beginning
// of the file entry specified. The code can then read this file, one sector at
// a time, by calling ReadFile(). Note that this code isn't re-entrant, and there
// is only one set of current file data, so only one file can be opened for reading
// at a time. In an MP3 player, that isn't a problem...
//--
PUBLIC void OpenFile (PXDIRENT pxDirEnt)
{
LONG lTemp;
m_wCurFileCluster = WSWAP(pxDirEnt->wFirstCluster);
m_lCurFileSector = SectorFromCluster(m_wCurFileCluster);
m_nRelFileSector = -1;
lTemp = pxDirEnt->lFileSize; m_lFileBytesLeft = LSWAP(lTemp);
//DBGOUT(("OpenFile: reading file \"%8.8s.%3.3s\",", &(pxDirEnt->szFileName), &(pxDirEnt->szFileType)));
//DBGOUT((" starting LBN=%ld, file size=%ld\n", m_lCurFileSector, m_lFileBytesLeft));
}
//++
// This routine will find the next sector in the currently open file. If
// all the sectors in the current cluster have been read, then the FAT will
// be consulted to find the next sector in the file. If there are no more
// clusters (i.e. the FAT entry is EOF_CLUSTER), then FALSE is returned.
// This last condition can't be used as an indication of end-of-file, however,
// because files rarely if ever exactly fill a cluster. The application must
// keep count of the number of bytes read and use the file size from the
// directory entry to determine EOF instead.
//--
PRIVATE BOOL NextFileSector (void)
{
// Note that OpenFile() sets nRelSector to -1, so the first time this
// routine is called we'll end up reading the first sector in the file...
if (m_wCurFileCluster == EOF_CLUSTER) return FALSE;
if (++m_nRelFileSector >= m_bVolClusterSize) {
m_nRelFileSector = 0;
m_wCurFileCluster = NextCluster(m_wCurFileCluster);
if (m_wCurFileCluster == EOF_CLUSTER) return FALSE;
m_lCurFileSector = SectorFromCluster(m_wCurFileCluster);
}
return TRUE;
}
//++
// This routine reads the currently opened file (remember that there can be
// only one file open at a time!) sector by sector. The pxBuffer parameter
// should point to a 512 byte buffer, and it will return the number of bytes
// actually read. Normally this will be 512, however it'll be less when we
// reach the last block in the file.
//--
PUBLIC WORD ReadFile (PXBYTE pxBuffer)
{
LONG lSector;
// If there are no bytes left in this file, then we always fail...
if (m_lFileBytesLeft == 0) return 0;
// We should never find EOF before the byte count reaches zero!
VERIFY( (NextFileSector()), ("ReadFile: EOF_CLUSTER found before byte count = 0\n") );
// Now we can read the sector. If this fails for some reason (it shouldn't!)
// the return zero and fake EOF...
lSector = m_lCurFileSector + (LONG) (m_nRelFileSector);
if (!ReadSector(lSector, pxBuffer)) {
DBGOUT(("ReadFile: error reading sector %ld from current file!\n", lSector));
return 0;
}
// We've read another sector, so decrement the number of bytes left in the
// file accordingly. If the number of bytes left is currently less than 512,
// then we've only read a partial sector (the last sector of the file wasn't
// full) and we have to zero the bytes left and return the appropriate count.
if (m_lFileBytesLeft >= IDE_SECTOR_SIZE) {
m_lFileBytesLeft -= IDE_SECTOR_SIZE; return IDE_SECTOR_SIZE;
} else {
LONG lTemp = m_lFileBytesLeft; m_lFileBytesLeft = 0;
return lTemp;
}
}
//++
// This routine will read the last cbBuffer bytes from the end of the currently
// opened file. Ordinarily you'd do an lseek() and then a read(), but, of course,
// we have no lseek() here. This is a rather esoteric function, but the purpose
// is obvious - it's used to read ID3 tags!
//
// The bad thing about a FAT file system is that there's no way to find the last
// cluster in a file except by following the entire cluster chain from the start.
// Unfortunately, there's just no way to make this efficient. With FAT16 a typical
// 512Mb CompactFlash card will have 8K byte sectors (16K byte for a 1Gb card) and
// at that rate, a typical 2 or 3Mb MP3 file will contain 300 to 400 clusters. With
// hundreds of clusters involved, finding the end of the file can take a long time.
//
// Notice that we need a temporary disk buffer for reading sectors from the MP3
// file. We pull one off the free buffer list, but we're careful to always return
// it before leaving!
//--
PUBLIC BOOL ReadFileEnd (PXBYTE pxBuffer, WORD cbBuffer) STACKSLOCS
{
WORD wCount; LONG lClusterSize, lSizeLimit; PIBCB pBCB;
ALLOCATE_BUFFER(pBCB);
if (pBCB == NULL) return FALSE;
// First, we want to get as close as we can to the end of the file with
// out actually passing the ID3 tag. Remember that there's a possibility
// that the ID3 tag might be split across the last two sectors in the file,
// and if that happens there's an even smaller possibility that the last
// two sectors might be in different clusters! But we know that as long as
// there are at least the cluster size plus the tag size bytes left in the
// file, there's no way we could have passed it.
//
// The lSizeLimit variable is just an optimization because the compiler
// isn't smart enough to factor out the calculation from the while loop.
// Remember that on an 8051, LONGs are quadruple precision arithmetic!
lClusterSize = (LONG) m_bVolClusterSize * (LONG) IDE_SECTOR_SIZE;
lSizeLimit = lClusterSize + cbBuffer;
while (m_lFileBytesLeft >= lSizeLimit) {
m_wCurFileCluster = NextCluster(m_wCurFileCluster);
ASSERT( (m_wCurFileCluster != EOF_CLUSTER), ("ReadFileEnd: Premature EOF cluster\n") );
m_lFileBytesLeft -= (LONG) lClusterSize;
}
// Be sure that the rest of the m_CurFile block, especially the current
// sector information, is valid before we proceed. Otherwise ReadFile()
// will crash and burn horribly...
m_nRelFileSector = -1;
m_lCurFileSector = SectorFromCluster(m_wCurFileCluster);
// Now find the last (or the second to the last, if the tag is split) sector
// in the file. This loop is even slower than the last one, but unlike the
// last one, which can execute hundreds of times, this one never executes more
// than fifteen times (assuming 8K clusters).
while (m_lFileBytesLeft >= (IDE_SECTOR_SIZE+cbBuffer)) {
VERIFY ( (NextFileSector()), ("ReadFileEnd: Premature EOF sector\n") );
m_lFileBytesLeft -= IDE_SECTOR_SIZE;
}
// Ok, we're there. Read at least the first part of the tag into the buffer.
wCount = ReadFile(pBCB->pbBuffer);
ASSERT( (wCount > 0), ("ReadFileEnd: Failed to read last chunk in file\n") );
if (wCount == 0) {RELEASE_BUFFER(pBCB); return FALSE;}
// If that last ReadFile() reached the end of the file, then we know
// that we have the ID3 tag in the buffer now. All we need to do is find
// it and copy it to the ID3 tag buffer. However, if there are still bytes
// left in the file then we know we have only part of the tag and we must
// do another ReadFile() to get the rest.
if (m_lFileBytesLeft == 0) {
// The easy case - we got the whole tag in one sector...
ASSERT( (wCount >= cbBuffer), ("ReadFileEnd: wCount < cbBuffer\n") );
memcpy(pxBuffer, pBCB->pbBuffer+(wCount-cbBuffer), cbBuffer);
} else {
// This tag is split across the last two sectors in the file. Figure out
// how many bytes (of the tag) we got in this sector and extract those first.
WORD wFragmentSize;
//DBGOUT(("ReadFileEnd: fragmented, lBytesLeft=%ld\n", m_lFileBytesLeft));
ASSERT( (m_lFileBytesLeft < cbBuffer), ("ReadFileEnd: lBytesLeft >= cbBuffer\n") );
wFragmentSize = cbBuffer - m_lFileBytesLeft;
memcpy(pxBuffer, pBCB->pbBuffer+IDE_SECTOR_SIZE-wFragmentSize, wFragmentSize);
// And then do another read to get the rest. Remember that since the
// ID3 tag is the last thing in the file, ALL the bytes returned by this
// second ReadFile() must be part of the tag.
wCount = ReadFile(pBCB->pbBuffer);
ASSERT( (wCount > 0), ("ReadFileEnd: Failed to read part 2\n") );
ASSERT( (m_lFileBytesLeft == 0), ("ReadFileEnd: Second read didn't finish file\n") );
memcpy(pxBuffer+wFragmentSize, pBCB->pbBuffer, wCount);
}
// We can give up the temporary buffer now ....
RELEASE_BUFFER(pBCB);
return TRUE;
}
//++
// This routine exists only for completeness in abstracting the file system
// layer. In this particular implementation, it doesn't do much.
//--
PUBLIC void CloseFile (void)
{
m_wCurFileCluster = 0; m_nRelFileSector = 0;
m_lCurFileSector = m_lFileBytesLeft = 0;
//DBGOUT(("CloseFile: ...\n"));
}
//++
// This routine reads the current compact flash card to see if it contains
// a valid FAT16 file system and, if it does, it sets up the volume data
// globals with information we'll need to access this volume in the future.
// If the current CF card DOESN'T contain a FAT16 file system, then this
// function returns FALSE...
//--
PUBLIC BOOL InitializeFAT (void)
{
// Initialize all the local data structures. Notice that this code is a
// little careless because it simply initializes both m_pCurDirBCB and
// m_pCurFATBCB to NULL. It's important (critical!) that both these be
// initialized, but if InitializeFAT() were ever to be called more than
// once (say, if we actually supported hot swapping CF cards) then this
// would cause a big time memory leak since we'd just be throwing away
// any buffer that had been previously allocated. At the moment, though,
// InitializeFAT() is only ever called once, so this isn't a problem.
memset(g_szSystemType, 0, SYSTEM_TYPE_LENGTH);
memset(g_szVolumeLabel, 0, VOLUME_LABEL_LENGTH);
g_wTotalMP3Files = 0;
// Reset all the m_?VolData* variables ...
m_lVolFATOffset = m_lVolRootOffset = m_lVolDataOffset = 0;
m_lVolFATSize = m_lVolRootSize = 0; m_bVolClusterSize = 0;
// Reset all the m_?CurDir* variables ...
m_lCurDirSector = 0; m_pCurDirBCB = NULL;
m_bCurDirEntry = DIRECTORY_ENTRIES_PER_SECTOR;
// Reset all the m_?CurFAT* variables ...
m_lCurFATSector = 0; memset(m_awCurFAT, 0, sizeof(m_awCurFAT));
// Reset all the current file variables...
m_wCurFileCluster = 0; m_nRelFileSector = 0;
m_lCurFileSector = m_lFileBytesLeft = 0;
// The next step is to find the volume boot sector (aka VBS). Experimentally
// is seems like Windows formats any removable media device, regardless of the
// size, as if it were a big floppy disk. This means that the VBS goes in
// sector zero of the device. However, there's nothing that prevents someone
// from running FDISK on a compact flash drive, and will write a Master Boot
// Record (aka MBR) to sector zero instead. In this case the MBR contains a
// partition table, the partition table points to the first sector of each
// logical volume, and THAT sector then contains the VBS (there's a separate
// VBS for every partition).
if (!ReadVBS(ReadMBR())) return FALSE;
DBGOUT(("Found file system type \"%s\" ...\n", g_szSystemType));
return TRUE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -