/* Arduino FatReader Library
* Copyright (C) 2009 by William Greiman
*
* This file is part of the Arduino FatReader 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 FatReader Library. If not, see
* .
*/
#ifndef FatReader_h
#define FatReader_h
#include
#include
// flags for ls()
/** ls() flag to print modify date */
#define LS_FLAG_FRAGMENTED 1
/** ls() flag to print file size */
#define LS_SIZE 2
/** ls() flag for recursive list of subdirectories */
#define LS_R 4
// offsets for structures used in volume init
/** Offset to BIOS Parameter Block in FAT Boot Sector */
#define BPB_OFFSET 11
/** Byte count for part of BIOS Parameter Block to be read by init() */
#define BPB_COUNT 37
/** offset to partition table in mbr */
#define PART_OFFSET (512 - 64 - 2)
// format dir.name into name[13] as standard 8.3 string
void dirName(dir_t &dir, char name[]);
// Print name field of dir_t struct in 8.3 format
void printEntryName(dir_t &dir);
//------------------------------------------------------------------------------
/** \class FatVolume
* \brief FatVolume provides access to FAT volumes.
*/
class FatVolume {
/** Allow FatReader access to FatVolume private data. */
friend class FatReader;
uint8_t blocksPerCluster_;
uint32_t blocksPerFat_;
uint32_t clusterCount_;
uint32_t dataStartBlock_;
uint8_t fatCount_;
uint32_t fatStartBlock_;
uint8_t fatType_;
SdReader *rawDevice_;
uint16_t rootDirEntryCount_;
uint32_t rootDirStart_;
uint32_t totalBlocks_;
uint8_t chainIsContiguous(uint32_t cluster);
uint32_t chainSize(uint32_t cluster);
uint8_t isEOC(uint32_t cluster) {
return cluster >= (fatType_ == 16 ? FAT16EOC_MIN : FAT32EOC_MIN);
}
uint32_t nextCluster(uint32_t cluster);
uint8_t rawRead(uint32_t block, uint16_t offset, uint8_t *dst,
uint16_t count) {
return rawDevice_->readData(block, offset, dst, count);
}
uint8_t validCluster(uint32_t cluster) {
return (1 < cluster && cluster < (clusterCount_ + 2));
}
public:
/** Create an instance of FatVolume */
FatVolume(void) : fatType_(0) {}
/**
* Initialize a FAT volume. Try partition one first then try super
* floppy format.
*
* \param[in] dev The SdReader 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(SdReader &dev) { return init(dev, 1) ? 1 : init(dev, 0); }
uint8_t init(SdReader &dev, uint8_t part);
// inline functions that return volume info
/** \return The volume's cluster size in blocks. */
uint8_t blocksPerCluster(void) { return blocksPerCluster_; }
/** \return The number of blocks in one FAT. */
uint32_t blocksPerFat(void) { return blocksPerFat_; }
/** \return The total number of clusters in the volume. */
uint32_t clusterCount(void) { return clusterCount_; }
/** \return The logical block number for the start of file data. */
uint32_t dataStartBlock(void) { return dataStartBlock_; }
/** \return The number of FAT structures on the volume. */
uint8_t fatCount(void) { return fatCount_; }
/** \return The logical block number for the start of the first FAT. */
uint32_t fatStartBlock(void) { return fatStartBlock_; }
/** \return The FAT type of the volume. Values are 12, 16 or 32. */
uint8_t fatType(void) { return fatType_; }
/*!
@brief Raw device for this volume
@returns the raw device
*/
SdReader *rawDevice(void) { return rawDevice_; }
/** \return The number of entries in the root directory for FAT16 volumes. */
uint32_t rootDirEntryCount(void) { 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) { return rootDirStart_; }
/** \return The total number of blocks in the volume. */
uint32_t totalBlocks(void) { return totalBlocks_; }
};
//------------------------------------------------------------------------------
/** \class FatReader
* \brief FatReader implements a minimal FAT16/FAT32 file reader class.
*/
class FatReader {
// values for type_
/** File is contiguous file */
#define FILE_IS_CONTIGUOUS 0X08
/** File type mask */
#define FILE_TYPE_MASK 0X07
/** This FatReader has not been opened. */
#define FILE_TYPE_CLOSED 0X00
/** FatReader for a file */
#define FILE_TYPE_NORMAL 0X01
/** FatReader for a FAT16 root directory */
#define FILE_TYPE_ROOT16 0X02
/** FatReader for a FAT32 root directory */
#define FILE_TYPE_ROOT32 0X03
/** FatReader for a subdirectory */
#define FILE_TYPE_SUBDIR 0X04
/** Test value for directory type */
#define FILE_TYPE_MIN_DIR FILE_TYPE_ROOT16
uint8_t type_;
uint32_t fileSize_;
uint32_t readCluster_;
uint32_t readPosition_;
uint32_t firstCluster_;
FatVolume *vol_;
int16_t readBlockData(uint8_t *dst, uint16_t count);
void lsR(dir_t &d, uint8_t flags, uint8_t indent);
public:
/** Create an instance of FatReader. */
FatReader(void) : type_(FILE_TYPE_CLOSED) {}
void ls(uint8_t flags = 0);
uint8_t openRoot(FatVolume &vol);
uint8_t open(FatVolume &vol, dir_t &dir);
uint8_t open(FatReader &dir, char *name);
uint8_t open(FatReader &dir, uint16_t index);
void optimizeContiguous(void);
int16_t read(void *buf, uint16_t count);
int8_t readDir(dir_t &dir);
void rewind(void);
uint8_t seekCur(uint32_t pos);
// inline functions
/** Close this instance of FatReader. */
void close(void) { type_ = FILE_TYPE_CLOSED; }
/** \return The total number of bytes in a file or directory. */
uint32_t fileSize(void) { return fileSize_; }
/**
* Type of this FatReader. You should use isFile() or isDir()
* instead of type() if possible.
*
* \return The file or directory type.
*/
uint8_t fileType(void) { return type_ & FILE_TYPE_MASK; }
/** \return The first cluster number for a file or directory. */
uint32_t firstCluster(void) { return firstCluster_; }
/**
* \return True if the bit for optimized reads is set.
* See optimizeContiguous(). */
uint8_t isContiguous(void) { return type_ & FILE_IS_CONTIGUOUS; }
/** \return True if this is a FatReader for a directory else false */
uint8_t isDir(void) { return fileType() >= FILE_TYPE_MIN_DIR; }
/** \return True if this is a FatReader for a file else false */
uint8_t isFile(void) { return fileType() == FILE_TYPE_NORMAL; }
/** \return True if FatReader is for an open file/directory else false */
uint8_t isOpen(void) { return fileType() != FILE_TYPE_CLOSED; }
/** \return The current cluster number for a file or directory. */
uint32_t readCluster(void) { return readCluster_; }
/** \return The read position for a file or directory. */
uint32_t readPosition(void) { return readPosition_; }
/**
* Set the read position for a file or directory to \a pos.
*
* \param[in] pos The new read position in bytes from the beginning
* of the file.
*
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
*/
uint8_t seekSet(uint32_t pos) {
if (pos >= readPosition_)
return seekCur(pos - readPosition_);
rewind();
return seekCur(pos);
}
/*!
@brief get the parent volume
@returns the parent volume
*/
FatVolume *volume(void) { return vol_; }
};
#endif // FatReader_h