#pragma once #include #include #include "Types.h" namespace FAT32 { static inline uint16_t getU8(const uint8_t* src) {return src[0];} static inline uint16_t getU16(const uint8_t* src) {return src[0]<<0 | src[1]<<8;} static inline uint32_t getU32(const uint8_t* src) {return src[0]<<0 | src[1]<<8 | src[2]<<16 | src[3]<<24;} static inline size_t min(size_t a, size_t b) {return (aval = val;} operator uint32_t() const {return val;} inline bool isEndOfClusters() const { return (val & 0x0FFFFFFF) >= 0xFFFFFF8; // only the lower 28 bits are used } inline bool isFree() const { return val == 0; } void operator ++() {++val;} }; const ClusterNr END_OF_CLUSTERS = 0xFFFFFFF; /** header within the first 512 bytes */ struct FSHeader { // https://www.easeus.com/resource/fat32-disk-structure.htm uint8_t dummy1[11]; uint16_t bytesPerSector; // @0x0B; uint8_t sectorsPerCluster; // @0x0D; uint16_t numReservedSectors; // @0x0E; uint8_t numberOfFATs; // @0x10; uint8_t dummy2[4]; uint8_t mediaDescriptor; // @0x15; uint8_t dummy3[10]; uint32_t sectorsInPartition; // @0x20; uint32_t sectorsPerFAT; // @0x24; uint8_t dummy4[4]; uint32_t rootDirFirstCluster; // @0x2C } __attribute__((packed)); struct FSDesc { uint16_t bytesPerSector; uint8_t sectorsPerCluster; uint16_t numReservedSectors; uint8_t numberOfFATs; uint32_t sectorsPerFAT; ClusterNr rootDirFirstCluster; }; struct Precomputed { uint32_t bytesPerCluster; AbsPos startOfFAT; // absolute byte offset where the FAT begins AbsPos startOfFirstDataCluster; // absolute byte offset where the first cluster is AbsPos startOfFirstRootDirCluster; // absolute byte offset where the first root dir cluster is uint32_t dirEntriesPerSector; // number of directory entries that fit into a sector }; union Attributes { uint8_t raw; struct { uint8_t readOnly : 1; uint8_t hidden : 1; uint8_t system : 1; uint8_t volumeID : 1; uint8_t directory : 1; uint8_t archive : 1; uint8_t unused1 : 1; uint8_t unused2 : 1; } bits; } __attribute__((packed)); /** structure for a DirectoryEntry as defined in the FAT standard */ struct DirEntry { unsigned char name[8]; unsigned char ext[3]; Attributes attr; uint8_t dummy1[8]; uint16_t firstClusterHi; uint8_t dummy2[4]; uint16_t firstClusterLo; uint32_t size; // file size in bytes } __attribute__((packed)); /** combine a DirectoryEntry with its absolute location on disk */ struct DirEntryAt { AbsPos posOnDisk; DirEntry entry; bool valid; /** ctor for an invalid entry */ DirEntryAt() : valid(false) {} /** ctor for a valid entry */ DirEntryAt(AbsPos posOnDisk, DirEntry* entry) : posOnDisk(posOnDisk), entry(*entry), valid(true) {} static DirEntryAt invalid() {return DirEntryAt();} /** is this a valid entry? */ bool isValid() const {return valid;} uint32_t getSize() const {return entry.size;} void setSize(uint32_t size) {entry.size = size;} bool isUnused() const {return entry.name[0] == 0xE5;} bool isDirectory() const {return entry.attr.bits.directory;} bool isEndOfDirectory() const {return entry.name[0] == 0x00;} bool isLongFileName() const {return (entry.attr.raw & 0b1111) == 0b1111;} void setEndOfDirectory() {entry.name[0] = 0x00;} ClusterNr getFirstCluster() const {return entry.firstClusterHi<<16 | entry.firstClusterLo<<0;} void setFirstCluster(ClusterNr nr) {entry.firstClusterHi = (uint16_t)(nr << 16); entry.firstClusterLo = (uint16_t)(nr << 0);} std::string getName() const { char buf[16]; uint8_t pos = 0; for (uint8_t i = 0; i < 8; ++i) {if (entry.name[i] != ' ') {buf[pos++] = entry.name[i];}} buf[pos++] = '.'; for (uint8_t i = 0; i < 3; ++i) {if ( entry.ext[i] != ' ') {buf[pos++] = entry.ext[i];}} buf[pos] = 0; return std::string(buf); } void setName(const std::string& str) { // "zero" fill name and extension memset(entry.name, ' ', 8); memset(entry.ext, ' ', 3); auto pos = str.find('.'); for (size_t i = 0; i < min(pos, str.length()); ++i) { if (i >= 8) {break;} entry.name[i] = str[i]; } for (size_t i = 0; i < min(pos, str.length()); ++i) { if (i >= 3) {break;} entry.ext[i] = str[i+pos+1]; } } }; }