/* Arduino SdFat Library
   Copyright (C) 2009 by William Greiman

   This file is part of the Arduino SdFat Library

   This Library is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with the Arduino SdFat Library.  If not, see
   <http://www.gnu.org/licenses/>.
*/
#ifndef SdFat_h
#define SdFat_h
/**
   \file
   SdFile and SdVolume classes
*/
#if defined (__AVR__) || defined (__CPU_ARC__)
  #include <avr/pgmspace.h>
#endif
#include "Sd2Card.h"
#include "FatStructs.h"
#include <Print.h>
//------------------------------------------------------------------------------
/**
   Allow use of deprecated functions if non-zero
*/
#define ALLOW_DEPRECATED_FUNCTIONS 1
//------------------------------------------------------------------------------
// forward declaration since SdVolume is used in SdFile
class SdVolume;
//==============================================================================
// SdFile class

#ifdef O_RDONLY //ARDUINO_ARCH_MBED
#undef O_READ
#undef O_RDONLY
#undef O_WRITE
#undef O_WRONLY
#undef O_RDWR
#undef O_ACCMODE
#undef O_APPEND
#undef O_SYNC
#undef O_CREAT
#undef O_EXCL
#undef O_TRUNC
#endif

// flags for ls()
/** ls() flag to print modify date */
uint8_t const LS_DATE = 1;
/** ls() flag to print file size */
uint8_t const LS_SIZE = 2;
/** ls() flag for recursive list of subdirectories */
uint8_t const LS_R = 4;

// use the gnu style oflag in open()
/** open() oflag for reading */
uint8_t const O_READ = 0X01;
/** open() oflag - same as O_READ */
uint8_t const O_RDONLY = O_READ;
/** open() oflag for write */
uint8_t const O_WRITE = 0X02;
/** open() oflag - same as O_WRITE */
uint8_t const O_WRONLY = O_WRITE;
/** open() oflag for reading and writing */
uint8_t const O_RDWR = (O_READ | O_WRITE);
/** open() oflag mask for access modes */
uint8_t const O_ACCMODE = (O_READ | O_WRITE);
/** The file offset shall be set to the end of the file prior to each write. */
uint8_t const O_APPEND = 0X04;
/** synchronous writes - call sync() after each write */
uint8_t const O_SYNC = 0X08;
/** create the file if nonexistent */
uint8_t const O_CREAT = 0X10;
/** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */
uint8_t const O_EXCL = 0X20;
/** truncate the file to zero length */
uint8_t const O_TRUNC = 0X40;

// flags for timestamp
/** set the file's last access date */
uint8_t const T_ACCESS = 1;
/** set the file's creation date and time */
uint8_t const T_CREATE = 2;
/** Set the file's write date and time */
uint8_t const T_WRITE = 4;
// values for type_
/** This SdFile has not been opened. */
uint8_t const FAT_FILE_TYPE_CLOSED = 0;
/** SdFile for a file */
uint8_t const FAT_FILE_TYPE_NORMAL = 1;
/** SdFile for a FAT16 root directory */
uint8_t const FAT_FILE_TYPE_ROOT16 = 2;
/** SdFile for a FAT32 root directory */
uint8_t const FAT_FILE_TYPE_ROOT32 = 3;
/** SdFile for a subdirectory */
uint8_t const FAT_FILE_TYPE_SUBDIR = 4;
/** Test value for directory type */
uint8_t const FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT16;

/** date field for FAT directory entry */
static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) {
  return (year - 1980) << 9 | month << 5 | day;
}
/** year part of FAT directory date field */
static inline uint16_t FAT_YEAR(uint16_t fatDate) {
  return 1980 + (fatDate >> 9);
}
/** month part of FAT directory date field */
static inline uint8_t FAT_MONTH(uint16_t fatDate) {
  return (fatDate >> 5) & 0XF;
}
/** day part of FAT directory date field */
static inline uint8_t FAT_DAY(uint16_t fatDate) {
  return fatDate & 0X1F;
}
/** time field for FAT directory entry */
static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) {
  return hour << 11 | minute << 5 | second >> 1;
}
/** hour part of FAT directory time field */
static inline uint8_t FAT_HOUR(uint16_t fatTime) {
  return fatTime >> 11;
}
/** minute part of FAT directory time field */
static inline uint8_t FAT_MINUTE(uint16_t fatTime) {
  return (fatTime >> 5) & 0X3F;
}
/** second part of FAT directory time field */
static inline uint8_t FAT_SECOND(uint16_t fatTime) {
  return 2 * (fatTime & 0X1F);
}
/** Default date for file timestamps is 1 Jan 2000 */
uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1;
/** Default time for file timestamp is 1 am */
uint16_t const FAT_DEFAULT_TIME = (1 << 11);
//------------------------------------------------------------------------------
/**
   \class SdFile
   \brief Access FAT16 and FAT32 files on SD and SDHC cards.
*/
class SdFile : public Print {
  public:
    /** Create an instance of SdFile. */
    SdFile(void) : type_(FAT_FILE_TYPE_CLOSED) {}
    /**
       writeError is set to true if an error occurs during a write().
       Set writeError to false before calling print() and/or write() and check
       for true after calls to print() and/or write().
    */
    //bool writeError;
    /**
       Cancel unbuffered reads for this file.
       See setUnbufferedRead()
    */
    void clearUnbufferedRead(void) {
      flags_ &= ~F_FILE_UNBUFFERED_READ;
    }
    uint8_t close(void);
    uint8_t contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
    uint8_t createContiguous(SdFile* dirFile,
                             const char* fileName, uint32_t size);
    /** \return The current cluster number for a file or directory. */
    uint32_t curCluster(void) const {
      return curCluster_;
    }
    /** \return The current position for a file or directory. */
    uint32_t curPosition(void) const {
      return curPosition_;
    }
    /**
       Set the date/time callback function

       \param[in] dateTime The user's call back function.  The callback
       function is of the form:

       \code
       void dateTime(uint16_t* date, uint16_t* time) {
         uint16_t year;
         uint8_t month, day, hour, minute, second;

         // User gets date and time from GPS or real-time clock here

         // return date using FAT_DATE macro to format fields
     *   *date = FAT_DATE(year, month, day);

         // return time using FAT_TIME macro to format fields
     *   *time = FAT_TIME(hour, minute, second);
       }
       \endcode

       Sets the function that is called when a file is created or when
       a file's directory entry is modified by sync(). All timestamps,
       access, creation, and modify, are set when a file is created.
       sync() maintains the last access date and last modify date/time.

       See the timestamp() function.
    */
    static void dateTimeCallback(
      void (*dateTime)(uint16_t* date, uint16_t* time)) {
      dateTime_ = dateTime;
    }
    /**
       Cancel the date/time callback function.
    */
    static void dateTimeCallbackCancel(void) {
      // use explicit zero since NULL is not defined for Sanguino
      dateTime_ = 0;
    }
    /** \return Address of the block that contains this file's directory. */
    uint32_t dirBlock(void) const {
      return dirBlock_;
    }
    uint8_t dirEntry(dir_t* dir);
    /** \return Index of this file's directory in the block dirBlock. */
    uint8_t dirIndex(void) const {
      return dirIndex_;
    }
    static void dirName(const dir_t& dir, char* name);
    /** \return The total number of bytes in a file or directory. */
    uint32_t fileSize(void) const {
      return fileSize_;
    }
    /** \return The first cluster number for a file or directory. */
    uint32_t firstCluster(void) const {
      return firstCluster_;
    }
    /** \return True if this is a SdFile for a directory else false. */
    uint8_t isDir(void) const {
      return type_ >= FAT_FILE_TYPE_MIN_DIR;
    }
    /** \return True if this is a SdFile for a file else false. */
    uint8_t isFile(void) const {
      return type_ == FAT_FILE_TYPE_NORMAL;
    }
    /** \return True if this is a SdFile for an open file/directory else false. */
    uint8_t isOpen(void) const {
      return type_ != FAT_FILE_TYPE_CLOSED;
    }
    /** \return True if this is a SdFile for a subdirectory else false. */
    uint8_t isSubDir(void) const {
      return type_ == FAT_FILE_TYPE_SUBDIR;
    }
    /** \return True if this is a SdFile for the root directory. */
    uint8_t isRoot(void) const {
      return type_ == FAT_FILE_TYPE_ROOT16 || type_ == FAT_FILE_TYPE_ROOT32;
    }
    void ls(uint8_t flags = 0, uint8_t indent = 0);
    uint8_t makeDir(SdFile* dir, const char* dirName);
    uint8_t open(SdFile* dirFile, uint16_t index, uint8_t oflag);
    uint8_t open(SdFile* dirFile, const char* fileName, uint8_t oflag);

    uint8_t openRoot(SdVolume* vol);
    static void printDirName(const dir_t& dir, uint8_t width);
    static void printFatDate(uint16_t fatDate);
    static void printFatTime(uint16_t fatTime);
    static void printTwoDigits(uint8_t v);
    /**
       Read the next byte from a file.

       \return For success read returns the next byte in the file as an int.
       If an error occurs or end of file is reached -1 is returned.
    */
    int16_t read(void) {
      uint8_t b;
      return read(&b, 1) == 1 ? b : -1;
    }
    int16_t read(void* buf, uint16_t nbyte);
    int8_t readDir(dir_t* dir);
    static uint8_t remove(SdFile* dirFile, const char* fileName);
    uint8_t remove(void);
    /** Set the file's current position to zero. */
    void rewind(void) {
      curPosition_ = curCluster_ = 0;
    }
    uint8_t rmDir(void);
    uint8_t rmRfStar(void);
    /** Set the files position to current position + \a pos. See seekSet(). */
    uint8_t seekCur(uint32_t pos) {
      return seekSet(curPosition_ + pos);
    }
    /**
        Set the files current position to end of file.  Useful to position
        a file for append. See seekSet().
    */
    uint8_t seekEnd(void) {
      return seekSet(fileSize_);
    }
    uint8_t seekSet(uint32_t pos);
    /**
       Use unbuffered reads to access this file.  Used with Wave
       Shield ISR.  Used with Sd2Card::partialBlockRead() in WaveRP.

       Not recommended for normal applications.
    */
    void setUnbufferedRead(void) {
      if (isFile()) {
        flags_ |= F_FILE_UNBUFFERED_READ;
      }
    }
    uint8_t timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day,
                      uint8_t hour, uint8_t minute, uint8_t second);
    uint8_t sync(uint8_t blocking = 1);
    /** Type of this SdFile.  You should use isFile() or isDir() instead of type()
       if possible.

       \return The file or directory type.
    */
    uint8_t type(void) const {
      return type_;
    }
    uint8_t truncate(uint32_t size);
    /** \return Unbuffered read flag. */
    uint8_t unbufferedRead(void) const {
      return flags_ & F_FILE_UNBUFFERED_READ;
    }
    /** \return SdVolume that contains this file. */
    SdVolume* volume(void) const {
      return vol_;
    }
    size_t write(uint8_t b);
    size_t write(const void* buf, uint16_t nbyte);
    size_t write(const char* str);
    #ifdef __AVR__
    void write_P(PGM_P str);
    void writeln_P(PGM_P str);
    #endif
    int availableForWrite(void);
    //------------------------------------------------------------------------------
    #if ALLOW_DEPRECATED_FUNCTIONS
    // Deprecated functions  - suppress cpplint warnings with NOLINT comment
    /** \deprecated Use:
       uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
    */
    uint8_t contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) {  // NOLINT
      return contiguousRange(&bgnBlock, &endBlock);
    }
    /** \deprecated Use:
        uint8_t SdFile::createContiguous(SdFile* dirFile,
          const char* fileName, uint32_t size)
    */
    uint8_t createContiguous(SdFile& dirFile,  // NOLINT
                             const char* fileName, uint32_t size) {
      return createContiguous(&dirFile, fileName, size);
    }

    /**
       \deprecated Use:
       static void SdFile::dateTimeCallback(
         void (*dateTime)(uint16_t* date, uint16_t* time));
    */
    static void dateTimeCallback(
      void (*dateTime)(uint16_t& date, uint16_t& time)) {  // NOLINT
      oldDateTime_ = dateTime;
      dateTime_ = dateTime ? oldToNew : 0;
    }
    /** \deprecated Use: uint8_t SdFile::dirEntry(dir_t* dir); */
    uint8_t dirEntry(dir_t& dir) {
      return dirEntry(&dir); // NOLINT
    }
    /** \deprecated Use:
       uint8_t SdFile::makeDir(SdFile* dir, const char* dirName);
    */
    uint8_t makeDir(SdFile& dir, const char* dirName) {  // NOLINT
      return makeDir(&dir, dirName);
    }
    /** \deprecated Use:
       uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag);
    */
    uint8_t open(SdFile& dirFile, // NOLINT
                 const char* fileName, uint8_t oflag) {
      return open(&dirFile, fileName, oflag);
    }
    /** \deprecated  Do not use in new apps */
    uint8_t open(SdFile& dirFile, const char* fileName) {  // NOLINT
      return open(dirFile, fileName, O_RDWR);
    }
    /** \deprecated Use:
       uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag);
    */
    uint8_t open(SdFile& dirFile, uint16_t index, uint8_t oflag) {  // NOLINT
      return open(&dirFile, index, oflag);
    }
    /** \deprecated Use: uint8_t SdFile::openRoot(SdVolume* vol); */
    uint8_t openRoot(SdVolume& vol) {
      return openRoot(&vol); // NOLINT
    }

    /** \deprecated Use: int8_t SdFile::readDir(dir_t* dir); */
    int8_t readDir(dir_t& dir) {
      return readDir(&dir); // NOLINT
    }
    /** \deprecated Use:
       static uint8_t SdFile::remove(SdFile* dirFile, const char* fileName);
    */
    static uint8_t remove(SdFile& dirFile, const char* fileName) {  // NOLINT
      return remove(&dirFile, fileName);
    }
    //------------------------------------------------------------------------------
    // rest are private
  private:
    static void (*oldDateTime_)(uint16_t& date, uint16_t& time);  // NOLINT
    static void oldToNew(uint16_t* date, uint16_t* time) {
      uint16_t d;
      uint16_t t;
      oldDateTime_(d, t);
      *date = d;
      *time = t;
    }
    #endif  // ALLOW_DEPRECATED_FUNCTIONS
  private:
    // bits defined in flags_
    // should be 0XF
    static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC);
    // available bits
    static uint8_t const F_FILE_NON_BLOCKING_WRITE = 0X10;
    // a new cluster was added to the file
    static uint8_t const F_FILE_CLUSTER_ADDED = 0X20;
    // use unbuffered SD read
    static uint8_t const F_FILE_UNBUFFERED_READ = 0X40;
    // sync of directory entry required
    static uint8_t const F_FILE_DIR_DIRTY = 0X80;

    // make sure F_OFLAG is ok
    #if ((F_FILE_NON_BLOCKING_WRITE | F_FILE_CLUSTER_ADDED | F_FILE_UNBUFFERED_READ | F_FILE_DIR_DIRTY) & F_OFLAG)
#error flags_ bits conflict
    #endif  // flags_ bits

    // private data
    uint8_t   flags_;         // See above for definition of flags_ bits
    uint8_t   type_;          // type of file see above for values
    uint32_t  curCluster_;    // cluster for current file position
    uint32_t  curPosition_;   // current file position in bytes from beginning
    uint32_t  dirBlock_;      // SD block that contains directory entry for file
    uint8_t   dirIndex_;      // index of entry in dirBlock 0 <= dirIndex_ <= 0XF
    uint32_t  fileSize_;      // file size in bytes
    uint32_t  firstCluster_;  // first cluster of file
    SdVolume* vol_;           // volume where file is located

    // private functions
    uint8_t addCluster(void);
    uint8_t addDirCluster(void);
    dir_t* cacheDirEntry(uint8_t action);
    static void (*dateTime_)(uint16_t* date, uint16_t* time);
    static uint8_t make83Name(const char* str, uint8_t* name);
    uint8_t openCachedEntry(uint8_t cacheIndex, uint8_t oflags);
    dir_t* readDirCache(void);
};
//==============================================================================
// SdVolume class
/**
   \brief Cache for an SD data block
*/
union cache_t {
  /** Used to access cached file data blocks. */
  uint8_t  data[512];
  /** Used to access cached FAT16 entries. */
  uint16_t fat16[256];
  /** Used to access cached FAT32 entries. */
  uint32_t fat32[128];
  /** Used to access cached directory entries. */
  dir_t    dir[16];
  /** Used to access a cached MasterBoot Record. */
  mbr_t    mbr;
  /** Used to access to a cached FAT boot sector. */
  fbs_t    fbs;
};
//------------------------------------------------------------------------------
/**
   \class SdVolume
   \brief Access FAT16 and FAT32 volumes on SD and SDHC cards.
*/
class SdVolume {
  public:
    /** Create an instance of SdVolume */
    SdVolume(void) : allocSearchStart_(2), fatType_(0) {}
    /** Clear the cache and returns a pointer to the cache.  Used by the WaveRP
        recorder to do raw write to the SD card.  Not for normal apps.
    */
    static uint8_t* cacheClear(void) {
      cacheFlush();
      cacheBlockNumber_ = 0XFFFFFFFF;
      return cacheBuffer_.data;
    }
    /**
       Initialize a FAT volume.  Try partition one first then try super
       floppy format.

       \param[in] dev The Sd2Card where the volume is located.

       \return The value one, true, is returned for success and
       the value zero, false, is returned for failure.  Reasons for
       failure include not finding a valid partition, not finding a valid
       FAT file system or an I/O error.
    */
    uint8_t init(Sd2Card* dev) {
      return init(dev, 1) ? true : init(dev, 0);
    }
    uint8_t init(Sd2Card* dev, uint8_t part);

    // inline functions that return volume info
    /** \return The volume's cluster size in blocks. */
    uint8_t blocksPerCluster(void) const {
      return blocksPerCluster_;
    }
    /** \return The number of blocks in one FAT. */
    uint32_t blocksPerFat(void)  const {
      return blocksPerFat_;
    }
    /** \return The total number of clusters in the volume. */
    uint32_t clusterCount(void) const {
      return clusterCount_;
    }
    /** \return The shift count required to multiply by blocksPerCluster. */
    uint8_t clusterSizeShift(void) const {
      return clusterSizeShift_;
    }
    /** \return The logical block number for the start of file data. */
    uint32_t dataStartBlock(void) const {
      return dataStartBlock_;
    }
    /** \return The number of FAT structures on the volume. */
    uint8_t fatCount(void) const {
      return fatCount_;
    }
    /** \return The logical block number for the start of the first FAT. */
    uint32_t fatStartBlock(void) const {
      return fatStartBlock_;
    }
    /** \return The FAT type of the volume. Values are 12, 16 or 32. */
    uint8_t fatType(void) const {
      return fatType_;
    }
    /** \return The number of entries in the root directory for FAT16 volumes. */
    uint32_t rootDirEntryCount(void) const {
      return rootDirEntryCount_;
    }
    /** \return The logical block number for the start of the root directory
         on FAT16 volumes or the first cluster number on FAT32 volumes. */
    uint32_t rootDirStart(void) const {
      return rootDirStart_;
    }
    /** return a pointer to the Sd2Card object for this volume */
    static Sd2Card* sdCard(void) {
      return sdCard_;
    }
    //------------------------------------------------------------------------------
    #if ALLOW_DEPRECATED_FUNCTIONS
    // Deprecated functions  - suppress cpplint warnings with NOLINT comment
    /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev); */
    uint8_t init(Sd2Card& dev) {
      return init(&dev); // NOLINT
    }

    /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev, uint8_t vol); */
    uint8_t init(Sd2Card& dev, uint8_t part) {  // NOLINT
      return init(&dev, part);
    }
    #endif  // ALLOW_DEPRECATED_FUNCTIONS
    //------------------------------------------------------------------------------
  private:
    // Allow SdFile access to SdVolume private data.
    friend class SdFile;

    // value for action argument in cacheRawBlock to indicate read from cache
    static uint8_t const CACHE_FOR_READ = 0;
    // value for action argument in cacheRawBlock to indicate cache dirty
    static uint8_t const CACHE_FOR_WRITE = 1;

    static cache_t cacheBuffer_;        // 512 byte cache for device blocks
    static uint32_t cacheBlockNumber_;  // Logical number of block in the cache
    static Sd2Card* sdCard_;            // Sd2Card object for cache
    static uint8_t cacheDirty_;         // cacheFlush() will write block if true
    static uint32_t cacheMirrorBlock_;  // block number for mirror FAT
    //
    uint32_t allocSearchStart_;   // start cluster for alloc search
    uint8_t blocksPerCluster_;    // cluster size in blocks
    uint32_t blocksPerFat_;       // FAT size in blocks
    uint32_t clusterCount_;       // clusters in one FAT
    uint8_t clusterSizeShift_;    // shift to convert cluster count to block count
    uint32_t dataStartBlock_;     // first data block number
    uint8_t fatCount_;            // number of FATs on volume
    uint32_t fatStartBlock_;      // start block for first FAT
    uint8_t fatType_;             // volume type (12, 16, OR 32)
    uint16_t rootDirEntryCount_;  // number of entries in FAT16 root dir
    uint32_t rootDirStart_;       // root start block for FAT16, cluster for FAT32
    //----------------------------------------------------------------------------
    uint8_t allocContiguous(uint32_t count, uint32_t* curCluster);
    uint8_t blockOfCluster(uint32_t position) const {
      return (position >> 9) & (blocksPerCluster_ - 1);
    }
    uint32_t clusterStartBlock(uint32_t cluster) const {
      return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_);
    }
    uint32_t blockNumber(uint32_t cluster, uint32_t position) const {
      return clusterStartBlock(cluster) + blockOfCluster(position);
    }
    static uint8_t cacheFlush(uint8_t blocking = 1);
    static uint8_t cacheMirrorBlockFlush(uint8_t blocking);
    static uint8_t cacheRawBlock(uint32_t blockNumber, uint8_t action);
    static void cacheSetDirty(void) {
      cacheDirty_ |= CACHE_FOR_WRITE;
    }
    static uint8_t cacheZeroBlock(uint32_t blockNumber);
    uint8_t chainSize(uint32_t beginCluster, uint32_t* size) const;
    uint8_t fatGet(uint32_t cluster, uint32_t* value) const;
    uint8_t fatPut(uint32_t cluster, uint32_t value);
    uint8_t fatPutEOC(uint32_t cluster) {
      return fatPut(cluster, 0x0FFFFFFF);
    }
    uint8_t freeChain(uint32_t cluster);
    uint8_t isEOC(uint32_t cluster) const {
      return  cluster >= (fatType_ == 16 ? FAT16EOC_MIN : FAT32EOC_MIN);
    }
    uint8_t readBlock(uint32_t block, uint8_t* dst) {
      return sdCard_->readBlock(block, dst);
    }
    uint8_t readData(uint32_t block, uint16_t offset,
                     uint16_t count, uint8_t* dst) {
      return sdCard_->readData(block, offset, count, dst);
    }
    uint8_t writeBlock(uint32_t block, const uint8_t* dst, uint8_t blocking = 1) {
      return sdCard_->writeBlock(block, dst, blocking);
    }
    uint8_t isBusy(void) {
      return sdCard_->isBusy();
    }
    uint8_t isCacheMirrorBlockDirty(void) {
      return (cacheMirrorBlock_ != 0);
    }
};
#endif  // SdFat_h
