📄 fat.c
字号:
// we might as well accept it if we find one...
if ( (pxMBR->aPartitions[i].bFlags != 0x00)
&& (pxMBR->aPartitions[i].bFlags != 0x80) ) continue;
// If either of the LBA fields are zero (which normally means this disk uses
// CHS addressing) then we can't do anything with this partition. [BTW, notice
// that these two tests should technically have a LSWAP() in them, but when we're
// simply testing against zero the byte order makes no difference!]
if ( (pxMBR->aPartitions[i].lOffset == 0L)
|| (pxMBR->aPartitions[i].lSize == 0L) ) continue;
// The last test, which is the only real sanity test we do, is to verify
// that the last sector in the partition (i.e. the partition offset plus
// the partition size) is less than the physical size of the CF card. It
// doesn't prove the MBR is valid, but at least it means we won't crash if
// we try to read the VBS from this partition!
if (( LSWAP(pxMBR->aPartitions[i].lOffset)
+LSWAP(pxMBR->aPartitions[i].lSize)-1 ) > g_lCardSectors) continue;
// As far as we can see, the this partition is valid. Let's use it!
RELEASE_BUFFER(pBCB); return LSWAP(pxMBR->aPartitions[i].lOffset);
}
// Here if no MBR is found - release the disk buffer and return...
FAIL:
RELEASE_BUFFER(pBCB); return 0L;
}
//++
// This routine reads the Volume Boot Sector (aka VBS) from the flash card
// and extracts the information that we'll need in the future to access files
// on this device. Finding the VBS can be something of an issue - normally
// it's located in sector zero, however if the flash card has been partitioned
// with FDISK then sector zero will contain a Master Boot Record instead. In
// that case we have to read the partition table in the MBR, select the part-
// ition we're interested in, and use that to find the VBS.
//
// Note that this routine uses the current directory sector as a temporary
// buffer. This is clearly harmless, since we can't have a real current
// directory sector before we've read the volume data!
//--
PRIVATE BOOL ReadVBS (LONG lLBN) STACKSLOCS
{
PIBCB pBCB; BOOT_SECTOR XDATA *pxVBS; BYTE i;
// Allocate a temporary buffer for disk I/O. Once this is done (successfully)
// we don't dare return without releasing the buffer first! After that's done,
// read the boot sector.
ALLOCATE_BUFFER(pBCB);
if (pBCB == NULL) return FALSE;
//DBGOUT(("ReadVBS: reading volume boot sector from LBN %lu\n", lLBN));
if (!ReadSector(lLBN, pBCB->pbBuffer)) goto FAIL;
pxVBS = (BOOT_SECTOR XDATA *) pBCB->pbBuffer;
#ifdef SHOWBOOTSECTOR
ShowBootSector(pxVBS);
#endif
// Sanity check a bunch of stuff in the VBS and BPB to make sure this really
// is a valid volume boot sector...
if (WSWAP(pxVBS->wBootSignature) != 0xAA55) {
DBGOUT(("ReadVBS: Boot signature = 0x%04X (!= 0xAA55)...\n", WSWAP(pxVBS->wBootSignature)));
goto FAIL;
} else if (WSWAP(pxVBS->bpb.wBytesPerSector) != IDE_SECTOR_SIZE) {
DBGOUT(("ReadVBS: Bytes per Sector = %d (!= %d)...\n", WSWAP(pxVBS->bpb.wBytesPerSector), IDE_SECTOR_SIZE));
goto FAIL;
} else if (pxVBS->bpb.bMediaDescriptor != 0xF8) {
DBGOUT(("ReadVBS: Media type = 0x%02bX (!= 0xF8)...\n", pxVBS->bpb.bMediaDescriptor));
goto FAIL;
}
// Check the file system type to be sure it is FAT16...
strncpy(g_szSystemType, pxVBS->bpb.szSystemID, SYSTEM_TYPE_LENGTH-1);
for (i=SYSTEM_TYPE_LENGTH-2; (i>0) && (g_szSystemType[i]==' '); --i) g_szSystemType[i]='\0';
if (strncmp(pxVBS->bpb.szSystemID, "FAT16 ", 8) != 0) {
DBGOUT(("ReadVBS: File system type = %8.8s (!= FAT16)...\n", &(pxVBS->bpb.szSystemID)));
goto FAIL;
}
// Looks like a valid volume boot sector. We can use this information to
// compute the location of the first FAT (the only one we ever use; any
// seconday FATs are ignored!), the location of the root directory, and the
// first sector of the user file data area.
m_lVolFATOffset = lLBN + (LONG) WSWAP(pxVBS->bpb.wReservedSectors);
m_lVolFATSize = (LONG) pxVBS->bpb.bNumberFATs * (LONG) WSWAP(pxVBS->bpb.wSectorsPerFAT);
//DBGOUT(("ReadVBS: FAT starts at LBN %ld, length %ld sectors ...\n", m_lVolFATOffset, m_lVolFATSize));
m_lVolRootOffset = m_lVolFATOffset + m_lVolFATSize;
m_lVolRootSize = (LONG) WSWAP(pxVBS->bpb.wRootEntries) / DIRECTORY_ENTRIES_PER_SECTOR;
//DBGOUT(("ReadVBS: Root directory starts at LBN %ld, length %ld sectors ...\n", m_lVolRootOffset, m_lVolRootSize));
m_lVolDataOffset = m_lVolRootOffset + m_lVolRootSize;
//DBGOUT(("ReadVBS: User file data starts at LBN %ld ...\n", m_lVolDataOffset));
m_bVolClusterSize = pxVBS->bpb.bSectorsPerCluster;
//DBGOUT(("ReadVBS: Sectors per cluster = %bd ...\n", m_bVolClusterSize));
// Release the buffer and we're done!
RELEASE_BUFFER(pBCB); return TRUE;
// Here if we fail for any reason - don't you dare return without releasing the
// disk buffer first!!
FAIL:
RELEASE_BUFFER(pBCB); return FALSE;
}
//++
// This little routine will read the specified directory sector into the
// current directory buffer m_pCurDirBCB. If the latter is NULL, then it
// will attempt to allocate a buffer first, which will eventually be released
// by FinishDirScan(). If there's an I/O error (unlikely, but possible)
// it will return FALSE...
//--
PRIVATE BOOL ReadDirSector (LONG lSector)
{
//ASSERT((m_pCurDirBCB != NULL), ("ReadDirectorySector: no current directory BCB!\n"));
if (m_pCurDirBCB == NULL) {
ALLOCATE_BUFFER(m_pCurDirBCB);
if (m_pCurDirBCB == NULL) return FALSE;
//DBGOUT(("ReadDirSector: allocated buffer 0x%04X\n", (WORD) m_pCurDirBCB->pbBuffer));
}
if (!ReadSector(lSector, m_pCurDirBCB->pbBuffer)) {
DBGOUT(("ReadDirSector: Error reading directory sector %ld\n", lSector));
return FALSE;
}
m_lCurDirSector = lSector; return TRUE;
//DBGOUT(("ReadDirSector: read directory sector %ld ...\n", m_lCurDirSector));
}
//++
// Initialize all the m_?CurDir???? variables so that we can start scanning
// forward from the first entry in the root directory. The parameter, fForward,
// is TRUE to scan forward from the first file in the directory, and FALSE to
// start scanning backwards from the last file. The latter is used when the user
// turns the knob counter clockwise, for example.
//--
PRIVATE BOOL StartDirScan (BOOL fForward)
{
if (fForward) {
// Read the first sector and start at the beginning...
if (!ReadDirSector(m_lVolRootOffset)) return FALSE;
m_bCurDirEntry = 0;
} else {
// Read the last sector and start at the end...
if (!ReadDirSector(m_lVolDataOffset-1)) return FALSE;
m_bCurDirEntry = DIRECTORY_ENTRIES_PER_SECTOR-1;
}
//DBGOUT(("StartDirScan: m_lCurDirSector=%ld, m_bCurDirEntry=%bd, buffer=0x%04X\n",
// m_lCurDirSector, m_bCurDirEntry, (WORD) m_pCurDirBCB->pbBuffer));
return TRUE;
}
//++
// This routine should be called whenever we've processed all the entries
// in the current directory sector. It will read the next directory sector
// into m_pCurDirBCB and reset m_bCurDirEntry to zero so that we can continue
// processing the files in the next directory sector. When we reach the end
// of the root directory, it will return FALSE.
//--
PRIVATE BOOL ReadNextDirSector (void)
{
m_bCurDirEntry = 0;
if (m_lCurDirSector >= m_lVolDataOffset-1) return FALSE;
return ReadDirSector(m_lCurDirSector+1);
}
//++
// This is essentially the same as ReadNextDirSector() except that it (as if
// you couldn't guess) reads the previous directory segment. It used for scanning
// backwards, of course - either to find an LFN or when the user turns the knob
// counter clockwise. It returns FALSE when we reach the _beginnning_ of the
// root directory...
//--
PRIVATE BOOL ReadPrevDirSector (void)
{
m_bCurDirEntry = DIRECTORY_ENTRIES_PER_SECTOR-1;
if (m_lCurDirSector <= m_lVolRootOffset) return FALSE;
return ReadDirSector(m_lCurDirSector-1);
}
//++
// This routine will release the current directory sector buffer back into
// the free pool. It'll be re-allocated again the next time ReadDirSector()
// is called. Needless to say, you need to be careful to make sure m_pCurDirBCB
// isn't NULL before accessing it!
//--
PRIVATE void ReleaseDirBuffer (void)
{
if (m_pCurDirBCB != NULL) {
//DBGOUT(("ReleaseDirBuffer: releasing buffer 0x%04X\n", (WORD) m_pCurDirBCB->pbBuffer));
RELEASE_BUFFER(m_pCurDirBCB); m_pCurDirBCB = NULL;
}
}
//++
// This routine will return TRUE if the file is an MP3 file, and FALSE
// otherwise. It also ignores empty files, deleted files, (sub)directory
// files, and volume labels, regardless of their name. It's used by the
// NextMP3File() and PreviousMP3File() functions to decide what to skip.
//--
PRIVATE BOOL IsMP3File (PXDIRENT pxDirEnt)
{
if (((BYTE) pxDirEnt->szFileName[0]) == EMPTY_FILE) return FALSE;
if (((BYTE) pxDirEnt->szFileName[0]) == DELETED_FILE) return FALSE;
if ((pxDirEnt->bAttributes & (VOLUME_NAME|DIRECTORY_FILE|SYSTEM_FILE|HIDDEN_FILE)) != 0) return FALSE;
if ( (pxDirEnt->szFileType[0] != 'M')
|| (pxDirEnt->szFileType[1] != 'P')
|| (pxDirEnt->szFileType[2] != '3') ) return FALSE;
return TRUE;
}
//++
// This routine will traverse the entire volume root directory and a) count
// the number of MP3 files present, and b) find the volume label file (if any).
// It's admittedly a very special purpose routine (not much a general purpose
// FAT function at all!), but doing it this way allows us to find everything
// we need to know in just one pass thru the directory. On a volume with lots
// of files, the time savings are important...
//--
PUBLIC void GetVolumeData (void)
{
PXDIRENT pxDirEnt;
memset(g_szVolumeLabel, 0, VOLUME_LABEL_LENGTH); g_wTotalMP3Files = 0;
if (!StartDirScan(TRUE)) return;
while (TRUE) {
// If this is an empty or deleted file, then just skip it....
pxDirEnt = GetDirEntry(m_bCurDirEntry);
if (((BYTE) pxDirEnt->szFileName[0]) == EMPTY_FILE) goto NextFile;
if (((BYTE) pxDirEnt->szFileName[0]) == DELETED_FILE) goto NextFile;
// If it's a volume label, then save the name in g_szVolumeLabel and go
// on to the next file. Notice that we have to be careful here, because a
// file with both the VOLUME LABEL and the DIRECTORY attributes set is an
// LFN entry, and those are common!
if (pxDirEnt->bAttributes == VOLUME_NAME) {
//DBGOUT(("GetVolumeData: volume label = \"%8.8s%3.3s\" attr=0x%02bX\n",
// pxDirEnt->szFileName, pxDirEnt->szFileType, pxDirEnt->bAttributes));
strncpy(g_szVolumeLabel, pxDirEnt->szFileName, VOLUME_LABEL_LENGTH-1);
goto NextFile;
}
// If it's an MP3 file, count it. Otherwise, ignore it!
if (IsMP3File(pxDirEnt)) ++g_wTotalMP3Files;
// Find the next file in the directory....
NextFile:
if (++m_bCurDirEntry >= DIRECTORY_ENTRIES_PER_SECTOR) {
if (!ReadNextDirSector()) break;
}
}
ReleaseDirBuffer();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -