diff --git a/Debug.h b/Debug.h index 9453c0b..ad957fc 100644 --- a/Debug.h +++ b/Debug.h @@ -46,7 +46,7 @@ private: template 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); diff --git a/ext/sd/AccessHelper.h b/ext/sd/AccessHelper.h index 9fbdb86..416cdb8 100644 --- a/ext/sd/AccessHelper.h +++ b/ext/sd/AccessHelper.h @@ -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); } diff --git a/ext/sd/CMakeLists.txt b/ext/sd/CMakeLists.txt index ca4a428..9cdb58e 100755 --- a/ext/sd/CMakeLists.txt +++ b/ext/sd/CMakeLists.txt @@ -39,7 +39,7 @@ ADD_DEFINITIONS( -fstack-protector-all - -g3 + -g -O0 -DWITH_TESTS diff --git a/ext/sd/fat32/DirIterator.h b/ext/sd/fat32/DirIterator.h index ccd14e1..83e9127 100644 --- a/ext/sd/fat32/DirIterator.h +++ b/ext/sd/fat32/DirIterator.h @@ -20,8 +20,73 @@ public: } + DirEntryAt cur() { + AbsPos pos = fs.clusterToAbsPos(curCluster) + (curSectorInCluster * fs.desc.bytesPerSector) + (curEntryInSector * sizeof(DirEntry)); + DirEntry* dirEntry = reinterpret_cast(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(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; } diff --git a/ext/sd/fat32/FS.h b/ext/sd/fat32/FS.h index 7c74dae..150a6a3 100644 --- a/ext/sd/fat32/FS.h +++ b/ext/sd/fat32/FS.h @@ -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(&next)); - Log::addInfo(NAME, "nextCluster(%d) -> %d", clusterNr, next); + dev.read(pos, sizeof(ClusterNr), reinterpret_cast(&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(&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); + } + }; } diff --git a/ext/sd/fat32/File.h b/ext/sd/fat32/File.h index a9f24f3..223793b 100644 --- a/ext/sd/fat32/File.h +++ b/ext/sd/fat32/File.h @@ -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); } diff --git a/ext/sd/fat32/File2.h b/ext/sd/fat32/File2.h new file mode 100644 index 0000000..9d926f0 --- /dev/null +++ b/ext/sd/fat32/File2.h @@ -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 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 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 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; + + } + +}; diff --git a/ext/sd/fat32/FreeClusterIterator.h b/ext/sd/fat32/FreeClusterIterator.h index 49b575c..9ef3e30 100644 --- a/ext/sd/fat32/FreeClusterIterator.h +++ b/ext/sd/fat32/FreeClusterIterator.h @@ -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; + } } diff --git a/ext/sd/fat32/Structs.h b/ext/sd/fat32/Structs.h index e28e5c7..3f066ce 100644 --- a/ext/sd/fat32/Structs.h +++ b/ext/sd/fat32/Structs.h @@ -1,11 +1,58 @@ #pragma once +#include #include #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 (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; @@ -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;} } diff --git a/ext/sd/fat32/WriteFile.h b/ext/sd/fat32/WriteFile.h deleted file mode 100644 index 91cafce..0000000 --- a/ext/sd/fat32/WriteFile.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -class WriteFile { - -}; diff --git a/ext/sd/main.cpp b/ext/sd/main.cpp index 6370ffa..ab119a6 100644 --- a/ext/sd/main.cpp +++ b/ext/sd/main.cpp @@ -17,35 +17,61 @@ #include +//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 @@ -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 diff --git a/ext/sd/tests/Helper.h b/ext/sd/tests/Helper.h index 3c726cb..c3a453e 100644 --- a/ext/sd/tests/Helper.h +++ b/ext/sd/tests/Helper.h @@ -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; } diff --git a/ext/sd/tests/TestAccessHelper.cpp b/ext/sd/tests/TestAccessHelper.cpp index 609bdf4..32ea1db 100644 --- a/ext/sd/tests/TestAccessHelper.cpp +++ b/ext/sd/tests/TestAccessHelper.cpp @@ -5,12 +5,12 @@ #include "../AccessHelper.h" -/* + TEST (TestAccessHelper, read) { // read varying numbers of bytes at arbitrary locations - TestDevice dev; + TestDevice dev(4096); AccessHelper 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 ah(dev); // src data diff --git a/ext/sd/tests/TestCreate.cpp b/ext/sd/tests/TestCreate.cpp new file mode 100644 index 0000000..aadd09f --- /dev/null +++ b/ext/sd/tests/TestCreate.cpp @@ -0,0 +1,50 @@ +#include + +#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; + + size_t size = 32*1024*1024; + TestDevice dev(size); + BlockDev bDev(dev); + FAT32::FS fs(bDev, 0); + fs.setup(size); + + const char* test = "This is a test"; + FAT32::FS::File2 f1 = fs.create2("test1.txt"); + f1.write(strlen(test), (const uint8_t*)test, [](int percent) {}); + + FAT32::FS::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 + +}