huge work on FAT32, initial support for writing
This commit is contained in:
2
Debug.h
2
Debug.h
@@ -46,7 +46,7 @@ private:
|
||||
|
||||
template<typename... Args> static void add(char level, const char* module, const char* fmt, Args... args) {
|
||||
|
||||
char buf[128];
|
||||
char buf[4096];
|
||||
char* dst = buf;
|
||||
|
||||
dst = dst + sprintf(dst, "%c[%-10s] ", level, module);
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
}
|
||||
|
||||
/** write size bytes starting at addr using data from src, supports writing partial blocks by reading them first */
|
||||
uint32_t write(AbsPos addr, uint32_t size, uint8_t* src) {
|
||||
uint32_t write(AbsPos addr, uint32_t size, const uint8_t* src) {
|
||||
|
||||
Log::addInfo(NAME, "write(%d @ %d)", size, addr);
|
||||
|
||||
@@ -122,8 +122,8 @@ public:
|
||||
}
|
||||
|
||||
/** write a single block of 512 bytes. addr = byteAddr/512 */
|
||||
bool writeSingleBlock(LBA512 addr, uint8_t* dst) {
|
||||
return dev.writeSingleBlock(addr, dst);
|
||||
bool writeSingleBlock(LBA512 addr, const uint8_t* src) {
|
||||
return dev.writeSingleBlock(addr, src);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ ADD_DEFINITIONS(
|
||||
-fstack-protector-all
|
||||
|
||||
|
||||
-g3
|
||||
-g
|
||||
-O0
|
||||
|
||||
-DWITH_TESTS
|
||||
|
||||
@@ -20,8 +20,73 @@ public:
|
||||
|
||||
}
|
||||
|
||||
DirEntryAt cur() {
|
||||
AbsPos pos = fs.clusterToAbsPos(curCluster) + (curSectorInCluster * fs.desc.bytesPerSector) + (curEntryInSector * sizeof(DirEntry));
|
||||
DirEntry* dirEntry = reinterpret_cast<DirEntry*>(buf + (curEntryInSector * sizeof(DirEntry)));
|
||||
return DirEntryAt(pos, dirEntry);
|
||||
}
|
||||
|
||||
DirEntry* next() {
|
||||
/** get the next usable entry within the current directory */
|
||||
DirEntryAt nextUsable() {
|
||||
|
||||
while(true) {
|
||||
|
||||
DirEntryAt dea = next(false);
|
||||
|
||||
// check it
|
||||
if (!dea.isValid()) {return DirEntryAt::invalid();}
|
||||
if (dea.isLongFileName()) {continue;}
|
||||
if (dea.isUnused()) {continue;}
|
||||
if (dea.isEndOfDirectory()) {return DirEntryAt::invalid();}
|
||||
|
||||
// usable!
|
||||
return dea;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** get (or create) a free entry within the current directory */
|
||||
DirEntryAt nextFree() {
|
||||
|
||||
while(true) {
|
||||
|
||||
DirEntryAt dea = next(true);
|
||||
|
||||
// check it
|
||||
if (!dea.isValid()) {return DirEntryAt::invalid();}
|
||||
|
||||
if (dea.isUnused()) {
|
||||
|
||||
// switch from "unused" to something usable
|
||||
//dea.setName("NEW.ONE");
|
||||
//dea.entry.attr.raw = 0;
|
||||
//return dea;
|
||||
|
||||
}
|
||||
|
||||
if (dea.isEndOfDirectory()) {
|
||||
|
||||
// add a new EndOfDirectory entry afterwards
|
||||
DirEntryAt dea2 = next(true);
|
||||
dea2.setEndOfDirectory();
|
||||
fs.write(dea2);
|
||||
|
||||
// switch from "end of directory" to something usable
|
||||
dea.setName("NEW.ONE");
|
||||
dea.entry.attr.raw = 0;
|
||||
return dea;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
DirEntryAt next(bool allocIfNeeded) {
|
||||
|
||||
while(true) {
|
||||
|
||||
@@ -35,8 +100,23 @@ public:
|
||||
// end of cluster reached?
|
||||
if (curSectorInCluster >= fs.desc.sectorsPerCluster) {
|
||||
|
||||
// find next cluster
|
||||
curCluster = fs.getNextCluster(curCluster); // number of the next cluster (if any)
|
||||
// find next cluster (if any)
|
||||
const ClusterNr nextCluster = fs.getNextCluster(curCluster);
|
||||
|
||||
// reached end of clusters?
|
||||
if (nextCluster.isEndOfClusters()) {
|
||||
|
||||
// do not alloc new entries? -> done here
|
||||
if (!allocIfNeeded) {
|
||||
return DirEntryAt::invalid();
|
||||
} else {
|
||||
curCluster = fs.allocFreeCluster(curCluster);
|
||||
}
|
||||
|
||||
} else {
|
||||
curCluster = nextCluster;
|
||||
}
|
||||
|
||||
curSectorInCluster = 0;
|
||||
|
||||
}
|
||||
@@ -47,18 +127,10 @@ public:
|
||||
}
|
||||
|
||||
// the current entry
|
||||
DirEntry* dirEntry = reinterpret_cast<DirEntry*>(buf + (sizeof(DirEntry) * curEntryInSector));
|
||||
DirEntryAt dea = cur();
|
||||
++curEntryInSector;
|
||||
|
||||
// check it
|
||||
if (dirEntry->isLongFileName()) {continue;}
|
||||
if (dirEntry->isUnused()) {continue;}
|
||||
if (dirEntry->isEndOfDirectory()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// usable!
|
||||
return dirEntry;
|
||||
return dea;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -15,25 +15,51 @@ namespace FAT32 {
|
||||
BlockDev& dev;
|
||||
AbsOffset offset;
|
||||
|
||||
|
||||
FSDesc desc;
|
||||
Precomputed tmp;
|
||||
bool valid = false;
|
||||
|
||||
#include "FreeClusterIterator.h"
|
||||
FreeClusterIterator fci;
|
||||
|
||||
public:
|
||||
|
||||
#include "File.h"
|
||||
#include "File2.h"
|
||||
#include "DirIterator.h"
|
||||
#include "WriteFile.h"
|
||||
#include "FreeClusterIterator.h"
|
||||
|
||||
|
||||
|
||||
/** ctor with the absolute offset addr (in bytes) */
|
||||
FS(BlockDev& dev, AbsOffset offset) : dev(dev), offset(offset) {
|
||||
FS(BlockDev& dev, AbsOffset offset) : dev(dev), offset(offset), fci(*this) {
|
||||
init();
|
||||
}
|
||||
|
||||
void setup(uint32_t totalSize) {
|
||||
|
||||
uint8_t buf[512] = {0};
|
||||
buf[0x1FE] = 0x55;
|
||||
buf[0x1FF] = 0xAA;
|
||||
|
||||
FSHeader* header = (FSHeader*) buf;
|
||||
header->bytesPerSector = 512;
|
||||
header->numberOfFATs = 2;
|
||||
header->numReservedSectors = 32;
|
||||
header->rootDirFirstCluster = 2;
|
||||
header->mediaDescriptor = 0xF8; // hard disk
|
||||
header->sectorsInPartition = totalSize / 512;
|
||||
header->sectorsPerCluster = 8;
|
||||
header->sectorsPerFAT = 64; // 8 MB
|
||||
|
||||
dev.write(offset, 512, (const uint8_t*)header);
|
||||
|
||||
init();
|
||||
|
||||
// mark the root-dir cluster as used
|
||||
setNextCluster(2, END_OF_CLUSTERS);
|
||||
|
||||
}
|
||||
|
||||
/** is the detected FS valid? */
|
||||
bool isValid() const {
|
||||
return valid;
|
||||
@@ -45,19 +71,39 @@ namespace FAT32 {
|
||||
}
|
||||
|
||||
/** open the given file for reading*/
|
||||
File open(const DirEntry& de) {
|
||||
return File(*this, de.size, de.getFirstCluster());
|
||||
File open(const DirEntryAt& dea) {
|
||||
return File(*this, dea.getSize(), dea.getFirstCluster());
|
||||
}
|
||||
|
||||
// /** create a new file for writing, in the given directory */
|
||||
// WriteFile newFile(DirEntry& de, const uint32_t size) {
|
||||
// if (!de.isDirectory()) {return nullptr;}
|
||||
// uint32_t allocSize = 0;
|
||||
// FreeClusterIterator fci(*this);
|
||||
// while(allocSize < size) {
|
||||
// ClusterNr = fci.next();
|
||||
// }
|
||||
// }
|
||||
/** open the given file for reading*/
|
||||
File2 open2(const DirEntryAt& dea) {
|
||||
return File2(*this, dea);
|
||||
}
|
||||
|
||||
/** create a new file for writing, in the given directory */
|
||||
File2 create2(const char* name) {
|
||||
|
||||
ClusterNr dirStartCluster = 2; // TODO
|
||||
|
||||
//if (!dea.isValid()) {Log::addError(NAME, "create2(): not a valid DirEntry");}
|
||||
//if (!dea.isDirectory()) {Log::addError(NAME, "create2(): not a directory");}
|
||||
DirIterator di(*this, dirStartCluster);
|
||||
|
||||
// allocate a new directory entry for the file
|
||||
DirEntryAt dea2 = di.nextFree();
|
||||
dea2.setName(name);
|
||||
dea2.setSize(0);
|
||||
|
||||
// allocate the file's first cluster
|
||||
ClusterNr firstCluster = allocFreeCluster(0);
|
||||
dea2.setFirstCluster(firstCluster);
|
||||
|
||||
// write the file description
|
||||
write(dea2);
|
||||
|
||||
return File2(*this, dea2);
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -102,18 +148,40 @@ namespace FAT32 {
|
||||
}
|
||||
|
||||
/** determine the ClusterNr following the given ClusterNr */
|
||||
ClusterNr getNextCluster(ClusterNr clusterNr) {
|
||||
const AbsPos pos = tmp.startOfFAT + ((clusterNr) * sizeof(uint32_t));
|
||||
ClusterNr getNextCluster(const ClusterNr clusterNr) {
|
||||
const AbsPos pos = tmp.startOfFAT + (clusterNr * sizeof(ClusterNr));
|
||||
ClusterNr next = 0;
|
||||
dev.read(pos, 4, reinterpret_cast<uint8_t*>(&next));
|
||||
Log::addInfo(NAME, "nextCluster(%d) -> %d", clusterNr, next);
|
||||
dev.read(pos, sizeof(ClusterNr), reinterpret_cast<uint8_t*>(&next));
|
||||
Log::addInfo(NAME, "getNextCluster(%d) -> %d", clusterNr, next);
|
||||
return next;
|
||||
}
|
||||
|
||||
/** set the ClusterNr following clusterNr */
|
||||
void setNextCluster(const ClusterNr clusterNr, const ClusterNr next) {
|
||||
const AbsPos pos = tmp.startOfFAT + (clusterNr * sizeof(ClusterNr));
|
||||
dev.write(pos, sizeof(ClusterNr), reinterpret_cast<const uint8_t*>(&next));
|
||||
Log::addInfo(NAME, "setNextCluster(%d) -> %d", clusterNr, next);
|
||||
}
|
||||
|
||||
/** convert ClusterNr into an absolute position on disk */
|
||||
AbsPos clusterToAbsPos(ClusterNr clusterNr) {
|
||||
return tmp.startOfFirstDataCluster + ((clusterNr - 2) * desc.sectorsPerCluster * desc.bytesPerSector);
|
||||
}
|
||||
|
||||
/** allocate a new empty cluster, and register it. if prevNr == 0, it has NO previous cluster (first in line) */
|
||||
ClusterNr allocFreeCluster(ClusterNr prevNr = 0) {
|
||||
ClusterNr newCluster = fci.next(); // get/find a free cluster
|
||||
if (prevNr > 0) {
|
||||
setNextCluster(prevNr, newCluster); // follows the previous cluster (if any)
|
||||
}
|
||||
setNextCluster(newCluster, END_OF_CLUSTERS);// no clusters follow the new cluster
|
||||
return newCluster;
|
||||
}
|
||||
|
||||
/** write the given DirEntry back to disk */
|
||||
void write(const DirEntryAt& dea) {
|
||||
dev.write(dea.posOnDisk, sizeof(DirEntry), (const uint8_t*)&dea.entry);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,13 +7,15 @@ class File {
|
||||
uint32_t totalSize;
|
||||
|
||||
uint32_t curAbsPos = 0; // position withithin the whole file
|
||||
uint32_t curCluster = 0; // cluster we are currently reading
|
||||
ClusterNr curCluster = 0; // cluster we are currently reading
|
||||
uint32_t posInCluster = 0; // position within the current cluster
|
||||
|
||||
|
||||
|
||||
|
||||
public:
|
||||
|
||||
File(FS& fs, uint32_t size, uint32_t firstCluster) : fs(fs), totalSize(size), curCluster(firstCluster) {
|
||||
File(FS& fs, uint32_t size, ClusterNr firstCluster) : fs(fs), totalSize(size), curCluster(firstCluster) {
|
||||
Log::addInfo(NAME, "init @ cluster %d", firstCluster);
|
||||
}
|
||||
|
||||
|
||||
179
ext/sd/fat32/File2.h
Normal file
179
ext/sd/fat32/File2.h
Normal file
@@ -0,0 +1,179 @@
|
||||
class File2 {
|
||||
|
||||
static constexpr const char* NAME = "FAT32_File2";
|
||||
static constexpr const uint32_t F_EOF = 0xFFFFFFFF;
|
||||
|
||||
FS& fs;
|
||||
DirEntryAt dea;
|
||||
|
||||
uint32_t curAbsPos = 0; // absolute position withithin the file
|
||||
uint32_t posInCluster = 0; // position within the current cluster
|
||||
uint32_t iCurCluster = 0; // current cluster index (within list of clusters)
|
||||
|
||||
std::vector<ClusterNr> clusters; // all clusters the file uses
|
||||
|
||||
public:
|
||||
|
||||
File2(FS& fs, const DirEntryAt& dea) : fs(fs), dea(dea) {
|
||||
Log::addInfo(NAME, "init @ cluster %d", getFirstCluster());
|
||||
getAllClusters(dea.getFirstCluster());
|
||||
}
|
||||
|
||||
/** the file's USED size */
|
||||
uint32_t getSize() const {return dea.getSize();}
|
||||
|
||||
/** the file's ALLOCATED size */
|
||||
uint32_t getAllocatedSize() const {return clusters.size() * fs.tmp.bytesPerCluster;}
|
||||
|
||||
/** the file's first cluster */
|
||||
ClusterNr getFirstCluster() const {return dea.getFirstCluster();}
|
||||
|
||||
/** get the file's name */
|
||||
const std::string& getName() const {return dea.getName();}
|
||||
|
||||
|
||||
/** read x bytes from the file */
|
||||
uint32_t read(uint32_t size, uint8_t* dst, std::function<void(int)> callback) {
|
||||
|
||||
Log::addInfo(NAME, "read %d bytes", size);
|
||||
|
||||
uint32_t remaining = size;
|
||||
uint32_t totalRead = 0;
|
||||
|
||||
while(remaining) {
|
||||
const uint32_t read = _read(remaining, dst);
|
||||
if (read == F_EOF) {
|
||||
Log::addInfo(NAME, "EOF"); break;
|
||||
}
|
||||
remaining -= read;
|
||||
dst += read;
|
||||
totalRead += read;
|
||||
callback(totalRead*100/size);
|
||||
}
|
||||
|
||||
return totalRead;
|
||||
|
||||
}
|
||||
|
||||
uint32_t write(uint32_t size, const uint8_t* src, std::function<void(int)> callback) {
|
||||
|
||||
Log::addInfo(NAME, "write %d bytes", size);
|
||||
|
||||
uint32_t remaining = size;
|
||||
uint32_t totalWritten = 0;
|
||||
|
||||
while(remaining) {
|
||||
const uint32_t written = _write(remaining, src);
|
||||
remaining -= written;
|
||||
src += written;
|
||||
totalWritten += written;
|
||||
callback(totalWritten*100/size);
|
||||
}
|
||||
|
||||
// update the file header (size might have changed)
|
||||
fs.write(dea);
|
||||
|
||||
return totalWritten;
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void setSize(uint32_t size) {dea.setSize(size);}
|
||||
|
||||
/** fetch the list of all clusters the file is using */
|
||||
void getAllClusters(ClusterNr startCluster) {
|
||||
|
||||
// initial cluster
|
||||
clusters.push_back(startCluster);
|
||||
|
||||
// all following clusters
|
||||
while(true) {
|
||||
startCluster = fs.getNextCluster(startCluster);
|
||||
if (!startCluster.isEndOfClusters()) {
|
||||
clusters.push_back(startCluster);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// debug
|
||||
char buf[1024];
|
||||
char* dst = buf; dst += sprintf(dst, "clusters: ");
|
||||
for (ClusterNr nr : clusters) {dst += sprintf(dst, "%d ", nr);}
|
||||
Log::addInfo(NAME, buf);
|
||||
|
||||
}
|
||||
|
||||
int32_t _read(uint32_t readSize, uint8_t* dst) {
|
||||
|
||||
// EOF reached?
|
||||
if (curAbsPos >= getSize()) {
|
||||
return F_EOF;
|
||||
}
|
||||
|
||||
// end of current cluster reached? -> switch to the next one
|
||||
if (posInCluster == fs.tmp.bytesPerCluster) {
|
||||
++iCurCluster;
|
||||
posInCluster = 0;
|
||||
Log::addInfo(NAME, "next cluster [%d] %d", iCurCluster, clusters[iCurCluster]);
|
||||
}
|
||||
|
||||
// how many bytes are left in the current cluster?
|
||||
const uint32_t remainingInCluster = fs.tmp.bytesPerCluster - posInCluster;
|
||||
|
||||
// determine how many bytes to read
|
||||
const uint32_t toRead = std::min(remainingInCluster, readSize);
|
||||
|
||||
const uint32_t offset = fs.clusterToAbsPos(clusters[iCurCluster]) + posInCluster;
|
||||
const uint32_t read = fs.dev.read(offset, toRead, dst);
|
||||
posInCluster += read;
|
||||
curAbsPos += read;
|
||||
|
||||
return read;
|
||||
|
||||
}
|
||||
|
||||
int32_t _write(uint32_t writeSize, const uint8_t* src) {
|
||||
|
||||
if (curAbsPos >= getAllocatedSize()) {
|
||||
if (clusters.empty()) {
|
||||
const ClusterNr newCluster = fs.allocFreeCluster(0);
|
||||
clusters.push_back(newCluster);
|
||||
Log::addInfo(NAME, "allocFirstCluster: %d", newCluster);
|
||||
} else {
|
||||
const ClusterNr prevCluster = clusters.back();
|
||||
const ClusterNr newCluster = fs.allocFreeCluster(prevCluster);
|
||||
clusters.push_back(newCluster);
|
||||
Log::addInfo(NAME, "allocNextCluster(%d): %d", prevCluster, newCluster);
|
||||
}
|
||||
dea.setSize(dea.getSize() + fs.tmp.bytesPerCluster);
|
||||
fs.write(dea);
|
||||
}
|
||||
|
||||
// end of current cluster reached? -> switch to the next one
|
||||
if (posInCluster == fs.tmp.bytesPerCluster) {
|
||||
++iCurCluster;
|
||||
posInCluster = 0;
|
||||
Log::addInfo(NAME, "next cluster [%d] %d", iCurCluster, clusters[iCurCluster]);
|
||||
}
|
||||
|
||||
// how many bytes are left in the current cluster?
|
||||
const uint32_t remainingInCluster = fs.tmp.bytesPerCluster - posInCluster;
|
||||
|
||||
// determine how many bytes to write
|
||||
const uint32_t toWrite = std::min(remainingInCluster, writeSize);
|
||||
|
||||
const uint32_t offset = fs.clusterToAbsPos(clusters[iCurCluster]) + posInCluster;
|
||||
const uint32_t written = fs.dev.write(offset, toWrite, src);
|
||||
posInCluster += written;
|
||||
curAbsPos += written;
|
||||
|
||||
// adjust the number of bytes used
|
||||
if (this->getSize() < curAbsPos) {this->setSize(curAbsPos);}
|
||||
|
||||
return written;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
@@ -4,10 +4,22 @@
|
||||
class FreeClusterIterator {
|
||||
|
||||
FS& fs;
|
||||
ClusterNr cur;
|
||||
|
||||
public:
|
||||
|
||||
FreeClusterIterator(FS& fs) : fs(fs) {
|
||||
FreeClusterIterator(FS& fs) : fs(fs), cur(2) {
|
||||
|
||||
}
|
||||
|
||||
/** get the next free cluster that is available */
|
||||
ClusterNr next() {
|
||||
|
||||
while(true) {
|
||||
const ClusterNr tmp = fs.getNextCluster(cur);
|
||||
if (tmp.isFree()) {return cur;}
|
||||
++cur;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include "Types.h"
|
||||
|
||||
namespace FAT32 {
|
||||
|
||||
using ClusterNr = uint32_t;
|
||||
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 (a<b) ? a : b;}
|
||||
|
||||
|
||||
struct ClusterNr {
|
||||
|
||||
uint32_t val;
|
||||
|
||||
ClusterNr() : val(0) {}
|
||||
ClusterNr(uint32_t val) : val(val) {}
|
||||
|
||||
void operator = (const uint32_t val) {this->val = 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;
|
||||
@@ -38,8 +85,8 @@ namespace FAT32 {
|
||||
} 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;
|
||||
@@ -48,28 +95,69 @@ namespace FAT32 {
|
||||
uint8_t dummy2[4];
|
||||
uint16_t firstClusterLo;
|
||||
uint32_t size; // file size in bytes
|
||||
} __attribute__((packed));
|
||||
|
||||
bool isUnused() const {return name[0] == 0xE5;}
|
||||
bool isDirectory() const {return attr.bits.directory;}
|
||||
bool isEndOfDirectory() const {return name[0] == 0x00;}
|
||||
bool isLongFileName() const {return (attr.raw & 0b1111) == 0b1111;}
|
||||
/** combine a DirectoryEntry with its absolute location on disk */
|
||||
struct DirEntryAt {
|
||||
|
||||
uint32_t getFirstCluster() const {return firstClusterHi<<16 | firstClusterLo<<0;}
|
||||
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 (name[i] != ' ') {buf[pos++] = name[i];}}
|
||||
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 ( ext[i] != ' ') {buf[pos++] = ext[i];}}
|
||||
for (uint8_t i = 0; i < 3; ++i) {if ( entry.ext[i] != ' ') {buf[pos++] = entry.ext[i];}}
|
||||
buf[pos] = 0;
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
} __attribute__((packed));
|
||||
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];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
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;}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
class WriteFile {
|
||||
|
||||
};
|
||||
@@ -17,35 +17,61 @@
|
||||
|
||||
#include <fstream>
|
||||
|
||||
//class Simu {
|
||||
|
||||
// FILE* f;
|
||||
|
||||
//public:
|
||||
|
||||
// Simu(const char* image) {
|
||||
// f = fopen(image, "rw");
|
||||
// if (!f) {throw std::runtime_error("failed to open");}
|
||||
// }
|
||||
|
||||
// uint32_t readSingleBlock(LBA512 addr, uint8_t* dst) {
|
||||
// Log::addInfo("SD", "read512(%d*512)", addr);
|
||||
// fseek(f, addr*512, SEEK_SET);
|
||||
// return fread(dst, 512, 1, f) * 512;
|
||||
// }
|
||||
|
||||
// uint32_t writeSingleBlock(LBA512 addr, const uint8_t* src) {
|
||||
// Log::addInfo("SD", "write512(%d*512)", addr);
|
||||
// fseek(f, addr*512, SEEK_SET);
|
||||
// return fwrite(src, 512, 1, f) * 512;
|
||||
// }
|
||||
|
||||
//};
|
||||
|
||||
|
||||
class Simu {
|
||||
|
||||
FILE* f;
|
||||
uint8_t* data;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
Simu(const char* image) {
|
||||
f = fopen(image, "rw");
|
||||
FILE* f = fopen(image, "rw");
|
||||
if (!f) {throw std::runtime_error("failed to open");}
|
||||
fseek(f, 0L, SEEK_END);
|
||||
size_t size = ftell(f);
|
||||
fseek(f, 0L, SEEK_SET);
|
||||
data = (uint8_t*) malloc(size);
|
||||
fread(data, size, 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
// uint32_t readSingleBlock(uint32_t addr, uint32_t size, uint8_t* dst) {
|
||||
// debugMod2("SD", "read(%d @ %d)", size, addr);
|
||||
// fseek(f, addr, SEEK_SET);
|
||||
// return fread(dst, size, 1, f) * size;
|
||||
// }
|
||||
|
||||
uint32_t readSingleBlock(LBA512 addr, uint8_t* dst) {
|
||||
Log::addInfo("SD", "read512(%d*512)", addr);
|
||||
fseek(f, addr*512, SEEK_SET);
|
||||
return fread(dst, 512, 1, f) * 512;
|
||||
memcpy(dst, data+addr*512, 512);
|
||||
return 512;
|
||||
}
|
||||
|
||||
uint32_t writeSingleBlock(LBA512 addr, const uint8_t* src) {
|
||||
Log::addInfo("SD", "write512(%d*512)", addr);
|
||||
fseek(f, addr*512, SEEK_SET);
|
||||
return fwrite(src, 512, 1, f) * 512;
|
||||
memcpy(data+addr*512, src, 512);
|
||||
return 512;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
@@ -53,6 +79,7 @@ public:
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
::testing::GTEST_FLAG(filter) = "*TestCreate*";
|
||||
return RUN_ALL_TESTS();
|
||||
|
||||
// diff /tmp/ram/TETRIS.GB /apps/workspace/gbemu/tests/tetris.gb
|
||||
@@ -77,30 +104,39 @@ int main(int argc, char** argv) {
|
||||
};
|
||||
|
||||
FAT32FS::DirIterator dir = fat.getRoot();
|
||||
while(true) {
|
||||
while(false) {
|
||||
|
||||
FAT32::DirEntry* de = dir.next();
|
||||
if (!de) {break;}
|
||||
std::cout << de->getName() << std::endl;
|
||||
FAT32::DirEntryAt dea = dir.nextUsable();
|
||||
if (!dea.isValid()) {break;}
|
||||
|
||||
if (1==0) {
|
||||
FAT32FS::File f = fat.open(*de);
|
||||
FAT32FS::File2 f = fat.open2(dea);
|
||||
|
||||
uint8_t* bufff = (uint8_t*) malloc(1024*1024);
|
||||
uint32_t read = f.read(de->size, bufff, callback);
|
||||
if (1==0) {
|
||||
uint8_t* bufff = (uint8_t*) malloc(1024*1024);
|
||||
uint32_t read = f.read(f.getSize(), bufff, callback);
|
||||
|
||||
std::string name = de->getName();
|
||||
std::ofstream out("/tmp/ram/" + name);
|
||||
out.write((char*)bufff, read);
|
||||
out.close();
|
||||
std::string name = f.getName();
|
||||
std::ofstream out("/tmp/ram/" + name);
|
||||
out.write((char*)bufff, read);
|
||||
out.close();
|
||||
|
||||
free(bufff);
|
||||
free(bufff);
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//FAT32::DirEntryAt root = fat.getRoot().cur();
|
||||
FAT32FS::File2 file = fat.create2("tmp1.txt");
|
||||
|
||||
uint8_t src[128];
|
||||
file.write(128, src, [] (int percent) {} );
|
||||
|
||||
// diff /tmp/ram/TETRIS.GB /apps/workspace/gbemu/tests/tetris.gb
|
||||
// diff /tmp/ram/KIRBY1.GB /apps/workspace/gbemu/tests/Kirby\'s\ Dream\ Land\ \(USA\,\ Europe\).gb
|
||||
|
||||
|
||||
@@ -6,7 +6,23 @@
|
||||
|
||||
struct TestDevice {
|
||||
|
||||
uint8_t buf[4096];
|
||||
size_t size;
|
||||
uint8_t* buf;
|
||||
|
||||
TestDevice(size_t size) : size(size) {
|
||||
buf = (uint8_t*) malloc(size);
|
||||
memset(buf, 0, size);
|
||||
}
|
||||
|
||||
~TestDevice() {
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void toFile(const std::string& name) {
|
||||
FILE* f = fopen(name.c_str(), "w+");
|
||||
fwrite(buf, size, 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/** read a 512 byte block into dst */
|
||||
bool readSingleBlock(LBA512 addr, uint8_t* dst) {
|
||||
@@ -14,7 +30,7 @@ struct TestDevice {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeSingleBlock(LBA512 addr, uint8_t* src) {
|
||||
bool writeSingleBlock(LBA512 addr, const uint8_t* src) {
|
||||
memcpy(buf+addr*512, src, 512);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
#include "../AccessHelper.h"
|
||||
|
||||
|
||||
/*
|
||||
|
||||
TEST (TestAccessHelper, read) {
|
||||
|
||||
// read varying numbers of bytes at arbitrary locations
|
||||
|
||||
TestDevice dev;
|
||||
TestDevice dev(4096);
|
||||
AccessHelper<TestDevice> ah(dev);
|
||||
for (size_t i = 0; i < sizeof(dev.buf); ++i) {dev.buf[i] = i;}
|
||||
|
||||
@@ -33,14 +33,14 @@ TEST (TestAccessHelper, read) {
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
TEST (TestAccessHelper, write) {
|
||||
|
||||
// write varying numbers of bytes at arbitrary locations
|
||||
|
||||
const uint8_t MAGIC = 0xFF;
|
||||
TestDevice dev;
|
||||
TestDevice dev(4096);
|
||||
AccessHelper<TestDevice> ah(dev);
|
||||
|
||||
// src data
|
||||
|
||||
50
ext/sd/tests/TestCreate.cpp
Normal file
50
ext/sd/tests/TestCreate.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Helper.h"
|
||||
|
||||
#include "../AccessHelper.h"
|
||||
#include "../fat32/FS.h"
|
||||
|
||||
// dd if=/dev/zero of=/tmp/ram/orig.fat32 bs=8192 count=4096
|
||||
// mkfs.vfat -F 32 -R 32 -s 8 orig.fat32
|
||||
// hexdump -v -C -n 512 orig.fat32
|
||||
|
||||
|
||||
|
||||
TEST(TestCreate, structure) {
|
||||
|
||||
FAT32::FSHeader header;
|
||||
|
||||
ASSERT_EQ(0x0B, (uint8_t*)&header.bytesPerSector - (uint8_t*)&header);
|
||||
ASSERT_EQ(0x0D, (uint8_t*)&header.sectorsPerCluster - (uint8_t*)&header);
|
||||
ASSERT_EQ(0x0E, (uint8_t*)&header.numReservedSectors - (uint8_t*)&header);
|
||||
ASSERT_EQ(0x10, (uint8_t*)&header.numberOfFATs - (uint8_t*)&header);
|
||||
ASSERT_EQ(0x15, (uint8_t*)&header.mediaDescriptor - (uint8_t*)&header);
|
||||
ASSERT_EQ(0x20, (uint8_t*)&header.sectorsInPartition - (uint8_t*)&header);
|
||||
ASSERT_EQ(0x24, (uint8_t*)&header.sectorsPerFAT - (uint8_t*)&header);
|
||||
ASSERT_EQ(0x2C, (uint8_t*)&header.rootDirFirstCluster - (uint8_t*)&header);
|
||||
|
||||
}
|
||||
|
||||
TEST (TestCreate, write) {
|
||||
|
||||
using BlockDev = AccessHelper<TestDevice>;
|
||||
|
||||
size_t size = 32*1024*1024;
|
||||
TestDevice dev(size);
|
||||
BlockDev bDev(dev);
|
||||
FAT32::FS<BlockDev> fs(bDev, 0);
|
||||
fs.setup(size);
|
||||
|
||||
const char* test = "This is a test";
|
||||
FAT32::FS<BlockDev>::File2 f1 = fs.create2("test1.txt");
|
||||
f1.write(strlen(test), (const uint8_t*)test, [](int percent) {});
|
||||
|
||||
FAT32::FS<BlockDev>::File2 f2 = fs.create2("test2.txt");
|
||||
f2.write(strlen(test), (const uint8_t*)test, [](int percent) {});
|
||||
|
||||
dev.toFile("/tmp/ram/1.fat32");
|
||||
|
||||
// mount -t vfat 1.fat32 /mnt/fat/ && ls -l /mnt/fat/ && cat /mnt/fat/test1.txt && umount /mnt/fat
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user