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

📄 fat.c

📁 这项工程将让您把自己的MP3播放器平台
💻 C
📖 第 1 页 / 共 4 页
字号:

//++
//   This routine will attempt to get the LFN (that's Long File Name, if you've
// been out of touch for a while :-) that corresponds to the directory entry
// specified.  This is a slight kludge because it works only for the current
// directory entry - the one just returned by Prev/NextMP3File().  You can't
// save away a directory entry and then call this routine some time later.
// Sorry, but that's not a problem for the player.
//
//   LFNs are stored in bogus directory entries that appear just BEFORE the
// real directory entry.  Unfortunatly there's no guarantee that the entire
// LFN will appear in one sector, so it may be necessary for us to back up
// and re-read the previous directory sector to complete the LFN. 
//
//   Finally, LFNs are stored in Unicode with 16 bit characters.  We convert
// these to ASCII by the simple expedient of dropping the upper byte - that
// mostly works for all Western character sets...
//--
PRIVATE BOOL GetFileLFN (void) STACKSLOCS
{
  BYTE bSequence, bEntry, i, cbString;  LONG lOriginalDirectorySector;
  BOOL fLFNFound;  PXSTRING pxString;  LFN_DIRECTORY_ENTRY XDATA *pxLFN;
  
  ASSERT( (m_lCurDirSector != 0), ("Call NextMP3File() first!") );
  memset(g_szLongFileName, 0, MAX_LFN_LENGTH);  pxString = g_szLongFileName;
  cbString = MAX_LFN_LENGTH;  bSequence = 1; fLFNFound = FALSE;
  lOriginalDirectorySector = m_lCurDirSector;  bEntry = m_bCurDirEntry;
  //DBGOUT(("GetFileLFN: searching for LFN for file \"%8.8s.%3.3s\"\n",
  //  &(GetCurDirEntry()->szFileName), &(GetCurDirEntry()->szFileType) ));

  //   Start with the previous directory entry and keep reading backwards
  // (possibly re-reading the previous sector, if required) until we find
  // something which is NOT a LFN entry.
  while (TRUE) {
    if (bEntry == 0) {
      // Re-read the previous directory sector...
      if (m_lCurDirSector == m_lVolRootOffset) break;
      --m_lCurDirSector;  bEntry = DIRECTORY_ENTRIES_PER_SECTOR - 1;
      if (!ReadDirSector(m_lCurDirSector)) break;
    } else
      --bEntry;
    //   See if this entry is another LFN segment.  Note that LFN entries
    // actually have a checksum, which we could (and probably should!) actually
    // check to make sure this entry is valid.  We don't bother, however, because
    // the other checks are usually enough...
    pxLFN = (LFN_DIRECTORY_ENTRY XDATA *) GetDirEntry(bEntry);
    //DBGOUT(("GetFileLFN: checking, Attributes=0x%02bX, Sequence=%bd, MBZ1,2=%bd %u\n",
    //  pxLFN->bAttributes, pxLFN->bSequence, pxLFN->bMBZ1, pxLFN->wMBZ2 ));
    if (pxLFN->bAttributes != LFN_ATTRIBUTES) break;
    if ((pxLFN->bSequence & 0x3F) != bSequence) break;
    if ((pxLFN->bMBZ1 != 0) || (pxLFN->wMBZ2 != 0)) break;
    //   Ok, this looks like a real LFN entry.  Extract the characters from it
    // and store them in the caller's buffer.  Remember that Unicode character
    // 0xFFFF is used to signal the end of the string.  BTW, using the sequence
    // LOBYTE(WSWAP(x)) is kind of wasteful (and it's a little too much for C51
    // to optimize away) but it is portable.  You can't just replace it with
    // HIBYTE(x) because SDCC is little endian and there WSWAP() is a no-op.
    //
    //   Lastly, you might think it would be more effective to write a sub-
    // routine for this, but it would require pointers to awChars, pxString
    // (the variable itself, so it can be updated), and cbMaxString (again,
    // so that it can be updated) at very least.  It actually generates less
    // code on the 8051 to just write it out the long way.
    for (i = 0;  i < 5;  ++i) {
      if (pxLFN->awChars1[i] == 0xFFFF) goto EndOfLFN;
      if (--cbString == 0) goto EndOfLFN;
      *pxString++ = LOBYTE(WSWAP(pxLFN->awChars1[i]));
    }
    for (i = 0;  i < 6;  ++i) {
      if (pxLFN->awChars2[i] == 0xFFFF) goto EndOfLFN;
      if (--cbString == 0) goto EndOfLFN;
      *pxString++ = LOBYTE(WSWAP(pxLFN->awChars2[i]));
    }
    for (i = 0;  i < 2;  ++i) {
      if (pxLFN->awChars3[i] == 0xFFFF) goto EndOfLFN;
      if (--cbString == 0) goto EndOfLFN;
      *pxString++ = LOBYTE(WSWAP(pxLFN->awChars3[i]));
    }
    // Remember that we found an LFN and bump the sequence for the next one...
    fLFNFound = TRUE;  ++bSequence;
    //   If bit 0x40 if the sequence number is set, then this is the last
    // entry in this LFN and we can stop. 
    if ((pxLFN->bSequence & 0x40) != 0) goto EndOfLFN;
  }
EndOfLFN:

  //   If we've changed the directory sector, then be sure and restore the
  // original before we leave...
  if (m_lCurDirSector != lOriginalDirectorySector) {
    m_lCurDirSector = lOriginalDirectorySector;  ReadDirSector(m_lCurDirSector);
  }
#ifdef DEBUG
  if (fLFNFound) DBGOUT(("GetFileLFN: \"%s\"\n", g_szLongFileName));
#endif
  return fLFNFound;
}


//++
//   This routine will return the first MP3 file found in the root directory.
// Normally it's called once, when the player starts up, and then is followed
// by many calls to NextMP3File() or PrevMP3File().  It goes something like
// this:
//
//	PXDIRENT pxFile = FirstMP3File();
//	...
//	while (pxFile != NULL) {
//	  ...
//	  pxFile = NextMP3File();
//	  ... or ...
//	  pxFile = PrevMP3File();
//	}
//
//   This loop will normally execute endlessly playing the all the MP3 files in
// the root directory over and over again.  pxFile will be NULL only in the case
// of severe errors (i.e. a hardware failure reading the directory) - in all other
// cases, NextMP3File() or PrevMP3File() will loop back to the beginning or end of
// the root directory as required.  Perhaps not the most useful general purpose
// directory traversal code, but just what we want for an MP3 player!
//--
PUBLIC PXDIRENT FirstMP3File (void)
{
  ASSERT((m_lVolRootOffset != 0), ("Call InitializeFAT() first!"));
  if (!StartDirScan(TRUE)) return NULL;  
  return NextMP3File();
}

//++
//   This routine searches the current directory for the next MP3 file.
// The m_lCurDirSector and m_bCurDirEntry globals should already be set up
// and optionally, the current directory segment can be in memory in the
// buffer addressed by m_pCurDirBCB.  However if the latter is NULL, this
// routine will re-read the current directory sector before proceeding;
// this allows us to return the directory sector buffer to the free list
// while we're actually playing an MP3 file, and that gives us an extra
// buffer for MP3 data.
//
//   This routine automatically skips empty files, deleted files, any
// volume name file, any subdirectory file, and any file marked as system
// or hidden.  All other files, including read only files, will be returned.
// It's up to the caller to sort out the ones the application wants.
//
//   N.B.  When the end of the directory is reached, this routine simply
// loops back to the start and returns the same set of files all over again.
// It returns NULL only in the case of gross failure (e.g. CompactFlash read
// errors).  It's not that useful for general directory traversal, but it's
// just the thing for an MP3 player!!
//
//   Notice also that this will unfortunately loop forever (!!) if there are
// no MP3 files in the root directory of this volume, but we know that's not
// the case because the code has already checked this by calling GetVolumeData().
//
//   And one final word - note that return return value is supposed to be a
// pointer to the current entry in the directory buffer.  "Supposed to be", but
// it isn't really because we release the directory buffer while we're playing
// MP3s.  Instead, this routine makes a copy of just the current directory
// entry into a temporary buffer in XDATA and returns the address of that.
// The caller can't tell the difference either way...
//--
PUBLIC PXDIRENT NextMP3File (void)
{
  PXDIRENT pxDirEnt;
  //  If the directory sector buffer has been released, then re-read the
  // current directory sector before proceeding!
  if (m_pCurDirBCB == NULL) ReadDirSector(m_lCurDirSector);
  while (TRUE) {
    //   Advance m_bCurDirEntry to the next entry in this sector.  If we've
    // reached the end, then read the next sector.  If that fails (because
    // we've reached the end of the root directory) then start over again.
    if (++m_bCurDirEntry >= DIRECTORY_ENTRIES_PER_SECTOR)
      if (!ReadNextDirSector())
	if (!StartDirScan(TRUE)) return NULL;

    // We've got a file - see if it's an MP3 file ...
    pxDirEnt = GetDirEntry(m_bCurDirEntry);
    if (IsMP3File(pxDirEnt)) {
      //   Read the LFN for this file (if it has one), which we have to do while
      // the current directory sector is still in memory, and then cache the
      // file's information.  Release the directory segment, and return a pointer
      // to the cached file information...
      GetFileLFN();  memcpy(&m_CurDirEntCache, pxDirEnt, sizeof(DIRECTORY_ENTRY));
      ReleaseDirBuffer();  return &m_CurDirEntCache;
    }
  }
}

//++
//   This routine is essentially the same as NextMP3File(), except that it goes
// backwards rather than forwards.  It's used by the navigation knob to find the
// previous MP3 file.  When we reach the beginning of the directory, it loops
// back to the end and starts returning the same list of files all over again.
//--
PUBLIC PXDIRENT PrevMP3File (void)
{
  PXDIRENT pxDirEnt;
  if (m_pCurDirBCB == NULL) ReadDirSector(m_lCurDirSector);
  while (TRUE) {
    if (m_bCurDirEntry-- == 0)
      if (!ReadPrevDirSector())
	if (!StartDirScan(FALSE)) return NULL;
    pxDirEnt = GetDirEntry(m_bCurDirEntry);
    if (IsMP3File(pxDirEnt)) {
      GetFileLFN();  memcpy(&m_CurDirEntCache, pxDirEnt, sizeof(DIRECTORY_ENTRY));
      ReleaseDirBuffer();  return &m_CurDirEntCache;
    }
  }
}


//++
// Return the starting sector number for a specific cluster...
//--
PRIVATE LONG SectorFromCluster (CLUSTER wCluster)
{
  LONG lSector;
  lSector = ((LONG) (wCluster-CLUSTER_OFFSET)) * ((LONG) (m_bVolClusterSize)) + m_lVolDataOffset;
  //DBGOUT(("SectorFromCluster: Cluster %u mapped to sector %ld ...\n", wCluster, lSector));
  return lSector;
}

//++
//   The first cluster in a file is easily determined from its directory entry.
// If a file consists of more than one cluster, then the FAT entry for the first
// cluster contains the number of the next cluster; the FAT entry for the second
// cluster points to the third, and so on.  Given the current cluster in a file,
// this function will determine the next cluster, reading the FAT as necessary.
// Note that this depends solely on the current cluster; nothing else about the
// file matters (at least not here!).
//
//   Notice that the current FAT sector is kept in the buffer pointed to by
// m_pCurFATBCB.  Unlike the directory buffer, which is released while we're
// actually playing an MP3 file, the current FAT sector is ALWAYS kept in memory
// even while we're playing.  In principle we could release the FAT buffer too,
// but remember that we need to keep calling NextCluster() to read the MP3 file
// even while we're playing, and that would mean that we'd need to keep re-reading
// the current FAT sector while we play.  That's just too much overhead!
//--
PRIVATE WORD NextCluster (CLUSTER wCluster)
{
  LONG lFATSector;  BYTE bFATOffset; 
  //   The first step is to figure out which FAT location, both the FAT sector and
  // the offset within that sector, holds the entry for the current cluster.
  // IMPORTANT!  Note that right now, IDE_SECTOR_SIZE == 512, sizeof(CLUSTER) == 2,
  // and CLUSTERS_PER_SECTOR == 256.  So long as this is true (and it probably will
  // be until somebody implements FAT12 or FAT32), we can save ourselves a lot of
  // time here, especially on a microprocessor where long divisions are expensive!
  //lFATSector = (WCluster) / CLUSTERS_PER_SECTOR;
  //lFATSector = (wCluster) % CLUSTERS_PER_SECTOR;
  lFATSector = m_lVolFATOffset + HIBYTE(wCluster);
  bFATOffset = LOBYTE(wCluster);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -