⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fat.c

📁 这项工程将让您把自己的MP3播放器平台
💻 C
📖 第 1 页 / 共 4 页
字号:
    // 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 + -