From f753929e87bf900c358335265a301dccd82293c3 Mon Sep 17 00:00:00 2001 From: David Given Date: Tue, 30 Aug 2022 22:51:31 +0200 Subject: [PATCH 1/4] Writing files works in fatfs. --- lib/bytes.cc | 13 +++ lib/bytes.h | 3 + lib/image.cc | 17 ++++ lib/image.h | 3 + lib/readerwriter.cc | 59 ++++++----- lib/readerwriter.h | 2 +- lib/vfs/acorndfs.cc | 132 +++++++++++++------------ lib/vfs/applesingle.cc | 6 +- lib/vfs/brother120fs.cc | 167 ++++++++++++++++---------------- lib/vfs/cbmfs.cc | 146 +++++++++++++++------------- lib/vfs/fatfs.cc | 38 +++++++- lib/vfs/fluxsectorinterface.cc | 113 ++++++++++++++++++--- lib/vfs/imagesectorinterface.cc | 30 +++--- lib/vfs/machfs.cc | 34 +++++-- lib/vfs/sectorinterface.h | 4 + lib/vfs/vfs.cc | 66 ++++++++++++- lib/vfs/vfs.h | 62 ++++-------- lib/vfs/vfs.proto | 108 +++++++++++---------- src/build.mk | 1 + src/fe-getfile.cc | 4 +- src/fe-putfile.cc | 51 ++++++++++ src/fe-write.cc | 2 +- src/fileutils.cc | 8 +- src/fluxengine.cc | 2 + src/gui/mainwindow.cc | 2 +- 25 files changed, 685 insertions(+), 388 deletions(-) create mode 100644 src/fe-putfile.cc diff --git a/lib/bytes.cc b/lib/bytes.cc index a0d05953..a2d60ef3 100644 --- a/lib/bytes.cc +++ b/lib/bytes.cc @@ -128,6 +128,19 @@ uint8_t& Bytes::operator [] (unsigned pos) return (*_data)[pos]; } +Bytes Bytes::readFromFile(const std::string& filename) +{ + Bytes bytes; + ByteWriter bw(bytes); + + std::ifstream f(filename); + if (!f) + Error() << fmt::format("cannot open '{}': {}", filename, strerror(errno)); + bw += f; + + return bytes; +} + Bytes Bytes::slice(unsigned start, unsigned len) const { start += _low; diff --git a/lib/bytes.h b/lib/bytes.h index 87cf7f44..c9c7b3a1 100644 --- a/lib/bytes.h +++ b/lib/bytes.h @@ -19,6 +19,9 @@ public: Bytes* operator = (const Bytes& other); +public: + static Bytes readFromFile(const std::string& filename); + public: /* General purpose methods */ diff --git a/lib/image.cc b/lib/image.cc index d8b9ede6..8dcba116 100644 --- a/lib/image.cc +++ b/lib/image.cc @@ -15,6 +15,17 @@ Image::Image(std::set>& sectors) calculateSize(); } +bool Image::empty() const +{ + return _sectors.empty(); +} + +bool Image::contains(unsigned track, unsigned side, unsigned sectorid) const +{ + key_t key = std::make_tuple(track, side, sectorid); + return _sectors.find(key) != _sectors.end(); +} + std::shared_ptr Image::get(unsigned track, unsigned side, unsigned sectorid) const { static std::shared_ptr NONE; @@ -37,6 +48,12 @@ std::shared_ptr Image::put(unsigned track, unsigned side, unsigned secto return sector; } +void Image::erase(unsigned track, unsigned side, unsigned sectorid) +{ + key_t key = std::make_tuple(track, side, sectorid); + _sectors.erase(key); +} + void Image::calculateSize() { _geometry = {}; diff --git a/lib/image.h b/lib/image.h index 8c1d599c..3272038c 100644 --- a/lib/image.h +++ b/lib/image.h @@ -39,8 +39,11 @@ public: public: void calculateSize(); + bool empty() const; + bool contains(unsigned track, unsigned side, unsigned sectorId) const; std::shared_ptr get(unsigned track, unsigned side, unsigned sectorId) const; std::shared_ptr put(unsigned track, unsigned side, unsigned sectorId); + void erase(unsigned track, unsigned side, unsigned sectorId); const_iterator begin() const { return const_iterator(_sectors.cbegin()); } const_iterator end() const { return const_iterator(_sectors.cend()); } diff --git a/lib/readerwriter.cc b/lib/readerwriter.cc index 954a79f1..d14c0833 100644 --- a/lib/readerwriter.cc +++ b/lib/readerwriter.cc @@ -187,8 +187,6 @@ void writeTracks(FluxSink& fluxSink, producer, std::function verifier) { - Logger() << fmt::format("Writing to: {}", (std::string)fluxSink); - for (const auto& location : Mapper::computeLocations()) { testForEmergencyStop(); @@ -287,44 +285,53 @@ void writeTracksAndVerify(FluxSink& fluxSink, return false; } - auto wantedSectors = encoder.collectSectors(location, image); - std::sort(wantedSectors.begin(), - wantedSectors.end(), - sectorPointerSortPredicate); + Image wanted; + for (const auto& sector : encoder.collectSectors(location, image)) + wanted.put(sector->logicalTrack, sector->logicalSide, sector->logicalSector)->data = sector->data; - std::vector> gotSectors( - trackFlux->sectors.begin(), trackFlux->sectors.end()); - std::sort(gotSectors.begin(), - gotSectors.end(), - sectorPointerSortPredicate); - - if (!std::equal(gotSectors.begin(), - gotSectors.end(), - wantedSectors.begin(), - wantedSectors.end(), - sectorPointerEqualsPredicate)) - { - Logger() << "good read but the data doesn't match"; - return false; - } + for (const auto& sector : trackFlux->sectors) + { + const auto s = wanted.get(sector->logicalTrack, sector->logicalSide, sector->logicalSector); + if (!s) + { + Logger() << "spurious sector on verify"; + return false; + } + if (s->data != sector->data) + { + Logger() << "data mismatch on verify"; + return false; + } + wanted.erase(sector->logicalTrack, sector->logicalSide, sector->logicalSector); + } + if (!wanted.empty()) + { + Logger() << "missing sector on verify"; + return false; + } return true; }); } -void writeDiskCommand(std::shared_ptr image, +void writeDiskCommand(const Image& image, AbstractEncoder& encoder, FluxSink& fluxSink, AbstractDecoder* decoder, FluxSource* fluxSource) { + const Image* imagep = ℑ + std::unique_ptr remapped; if (config.has_sector_mapping()) - image = std::move(Mapper::remapSectorsLogicalToPhysical( - *image, config.sector_mapping())); + { + remapped = Mapper::remapSectorsLogicalToPhysical( + image, config.sector_mapping()); + imagep = &*remapped; + } if (fluxSource && decoder) - writeTracksAndVerify(fluxSink, encoder, *fluxSource, *decoder, *image); + writeTracksAndVerify(fluxSink, encoder, *fluxSource, *decoder, *imagep); else - writeTracks(fluxSink, encoder, *image); + writeTracks(fluxSink, encoder, *imagep); } void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink) diff --git a/lib/readerwriter.h b/lib/readerwriter.h index 5c088d69..5045bb6f 100644 --- a/lib/readerwriter.h +++ b/lib/readerwriter.h @@ -23,7 +23,7 @@ extern void fillBitmapTo(std::vector& bitmap, unsigned terminateAt, const std::vector& pattern); -extern void writeDiskCommand(std::shared_ptr image, +extern void writeDiskCommand(const Image& image, AbstractEncoder& encoder, FluxSink& fluxSink, AbstractDecoder* decoder = nullptr, diff --git a/lib/vfs/acorndfs.cc b/lib/vfs/acorndfs.cc index a95ac808..ebdef33f 100644 --- a/lib/vfs/acorndfs.cc +++ b/lib/vfs/acorndfs.cc @@ -6,30 +6,32 @@ class AcornDfsDirent : public Dirent { public: - AcornDfsDirent(int inode, const Bytes& bytes0, const Bytes& bytes1) - { - filename += (char)(bytes0[7] & 0x7f); - filename += '.'; - for (int j = 0; j < 7; j++) - filename += bytes0[j] & 0x7f; - filename = filename.substr(0, filename.find(' ')); + AcornDfsDirent(int inode, const Bytes& bytes0, const Bytes& bytes1) + { + filename += (char)(bytes0[7] & 0x7f); + filename += '.'; + for (int j = 0; j < 7; j++) + filename += bytes0[j] & 0x7f; + filename = filename.substr(0, filename.find(' ')); - this->inode = inode; - start_sector = ((bytes1[6] & 0x03) << 8) | bytes1[7]; - load_address = ((bytes1[6] & 0x0c) << 14) | (bytes1[1] << 8) | bytes1[0]; - exec_address = ((bytes1[6] & 0xc0) << 10) | (bytes1[3] << 8) | bytes1[2]; - locked = bytes0[7] & 0x80; - length = ((bytes1[6] & 0x30) << 12) | (bytes1[5] << 8) | bytes1[4]; - file_type = TYPE_FILE; - mode = locked ? "L" : ""; - } + this->inode = inode; + start_sector = ((bytes1[6] & 0x03) << 8) | bytes1[7]; + load_address = + ((bytes1[6] & 0x0c) << 14) | (bytes1[1] << 8) | bytes1[0]; + exec_address = + ((bytes1[6] & 0xc0) << 10) | (bytes1[3] << 8) | bytes1[2]; + locked = bytes0[7] & 0x80; + length = ((bytes1[6] & 0x30) << 12) | (bytes1[5] << 8) | bytes1[4]; + file_type = TYPE_FILE; + mode = locked ? "L" : ""; + } public: - int inode; - uint32_t start_sector; - uint32_t load_address; - uint32_t exec_address; - bool locked; + int inode; + uint32_t start_sector; + uint32_t load_address; + uint32_t exec_address; + bool locked; }; class AcornDfsFilesystem : public Filesystem @@ -37,7 +39,7 @@ class AcornDfsFilesystem : public Filesystem public: AcornDfsFilesystem( const AcornDfsProto& config, std::shared_ptr sectors): - Filesystem(sectors), + Filesystem(sectors), _config(config) { } @@ -53,46 +55,49 @@ public: throw FileNotFoundException(); std::vector> result; - for (auto& dirent : findAllFiles()) - result.push_back(std::move(dirent)); + for (auto& dirent : findAllFiles()) + result.push_back(std::move(dirent)); return result; } - Bytes getFile(const Path& path) - { - auto dirent = findFile(path); - int sectors = (dirent->length + 255) / 256; + Bytes getFile(const Path& path) + { + auto dirent = findFile(path); + int sectors = (dirent->length + 255) / 256; - Bytes data; - ByteWriter bw(data); - for (int i = 0; i < sectors; i++) - { - auto sector = getLogicalSector(dirent->start_sector + i); - bw.append(sector); - } + Bytes data; + ByteWriter bw(data); + for (int i = 0; i < sectors; i++) + { + auto sector = getLogicalSector(dirent->start_sector + i); + bw.append(sector); + } - data.resize(dirent->length); - return data; - } + data.resize(dirent->length); + return data; + } - std::map getMetadata(const Path& path) - { - std::map attributes; + std::map getMetadata(const Path& path) + { + std::map attributes; - auto dirent = findFile(path); - attributes["filename"] = dirent->filename; - attributes["length"] = fmt::format("{}", dirent->length); - attributes["type"] = "file"; - attributes["mode"] = dirent->mode; - attributes["acorndfs.inode"] = fmt::format("{}", dirent->inode); - attributes["acorndfs.start_sector"] = fmt::format("{}", dirent->start_sector); - attributes["acorndfs.load_address"] = fmt::format("0x{:x}", dirent->load_address); - attributes["acorndfs.exec_address"] = fmt::format("0x{:x}", dirent->exec_address); - attributes["acorndfs.locked"] = fmt::format("{}", dirent->locked); + auto dirent = findFile(path); + attributes["filename"] = dirent->filename; + attributes["length"] = fmt::format("{}", dirent->length); + attributes["type"] = "file"; + attributes["mode"] = dirent->mode; + attributes["acorndfs.inode"] = fmt::format("{}", dirent->inode); + attributes["acorndfs.start_sector"] = + fmt::format("{}", dirent->start_sector); + attributes["acorndfs.load_address"] = + fmt::format("0x{:x}", dirent->load_address); + attributes["acorndfs.exec_address"] = + fmt::format("0x{:x}", dirent->exec_address); + attributes["acorndfs.locked"] = fmt::format("{}", dirent->locked); - return attributes; - } + return attributes; + } private: std::vector> findAllFiles() @@ -110,25 +115,26 @@ private: auto bytes0 = sector0.slice(i * 8 + 8, 8); auto bytes1 = sector1.slice(i * 8 + 8, 8); - result.push_back(std::make_unique(i, bytes0, bytes1)); + result.push_back( + std::make_unique(i, bytes0, bytes1)); } return result; } - std::unique_ptr findFile(const Path& path) - { - if (path.size() != 1) - throw BadPathException(); + std::unique_ptr findFile(const Path& path) + { + if (path.size() != 1) + throw BadPathException(); - for (auto& dirent : findAllFiles()) + for (auto& dirent : findAllFiles()) { - if (dirent->filename == path[0]) - return std::move(dirent); + if (dirent->filename == path[0]) + return std::move(dirent); } - throw FileNotFoundException(); - } + throw FileNotFoundException(); + } private: const AcornDfsProto& _config; diff --git a/lib/vfs/applesingle.cc b/lib/vfs/applesingle.cc index cf9d98e2..ea40ca77 100644 --- a/lib/vfs/applesingle.cc +++ b/lib/vfs/applesingle.cc @@ -33,9 +33,9 @@ void AppleSingle::parse(const Bytes& bytes) case 9: { - Bytes finderinfo = bytes.slice(offset, length); - type = finderinfo.slice(0, 4); - creator = finderinfo.slice(4, 4); + Bytes finderinfo = bytes.slice(offset, length); + type = finderinfo.slice(0, 4); + creator = finderinfo.slice(4, 4); break; } diff --git a/lib/vfs/brother120fs.cc b/lib/vfs/brother120fs.cc index fb9e0fb2..9727d526 100644 --- a/lib/vfs/brother120fs.cc +++ b/lib/vfs/brother120fs.cc @@ -27,32 +27,32 @@ static constexpr int DIRECTORY_SECTORS = 8; class Brother120Dirent : public Dirent { public: - Brother120Dirent(int inode, const Bytes& bytes) - { - ByteReader br(bytes); - filename = br.read(8); - filename = filename.substr(0, filename.find(' ')); + Brother120Dirent(int inode, const Bytes& bytes) + { + ByteReader br(bytes); + filename = br.read(8); + filename = filename.substr(0, filename.find(' ')); - this->inode = inode; - brother_type = br.read_8(); - start_sector = br.read_be16(); - length = br.read_8() * SECTOR_SIZE; - file_type = TYPE_FILE; - mode = ""; - } + this->inode = inode; + brother_type = br.read_8(); + start_sector = br.read_be16(); + length = br.read_8() * SECTOR_SIZE; + file_type = TYPE_FILE; + mode = ""; + } public: - int inode; - int brother_type; - uint32_t start_sector; + int inode; + int brother_type; + uint32_t start_sector; }; class Brother120Filesystem : public Filesystem { public: - Brother120Filesystem( - const Brother120FsProto& config, std::shared_ptr sectors): - Filesystem(sectors), + Brother120Filesystem(const Brother120FsProto& config, + std::shared_ptr sectors): + Filesystem(sectors), _config(config) { } @@ -64,97 +64,98 @@ public: std::vector> list(const Path& path) { - if (!path.empty()) - throw FileNotFoundException(); + if (!path.empty()) + throw FileNotFoundException(); - std::vector> result; - for (auto& dirent : findAllFiles()) - result.push_back(std::move(dirent)); + std::vector> result; + for (auto& dirent : findAllFiles()) + result.push_back(std::move(dirent)); return result; } - Bytes getFile(const Path& path) - { - auto fat = readFat(); - auto dirent = findFile(path); - int sector = dirent->start_sector; + Bytes getFile(const Path& path) + { + auto fat = readFat(); + auto dirent = findFile(path); + int sector = dirent->start_sector; - Bytes data; - ByteWriter bw(data); - while ((sector != 0) && (sector != 0xffff)) - { - bw += getLogicalSector(sector - 1); - sector = fat.at(sector); - } + Bytes data; + ByteWriter bw(data); + while ((sector != 0) && (sector != 0xffff)) + { + bw += getLogicalSector(sector - 1); + sector = fat.at(sector); + } - return data; - } + return data; + } - std::map getMetadata(const Path& path) - { - std::map attributes; + std::map getMetadata(const Path& path) + { + std::map attributes; - auto dirent = findFile(path); - attributes["filename"] = dirent->filename; - attributes["length"] = fmt::format("{}", dirent->length); - attributes["type"] = "file"; - attributes["mode"] = dirent->mode; - attributes["brother120.inode"] = fmt::format("{}", dirent->inode); - attributes["brother120.start_sector"] = fmt::format("{}", dirent->start_sector); - attributes["brother120.type"] = fmt::format("{}", dirent->brother_type); + auto dirent = findFile(path); + attributes["filename"] = dirent->filename; + attributes["length"] = fmt::format("{}", dirent->length); + attributes["type"] = "file"; + attributes["mode"] = dirent->mode; + attributes["brother120.inode"] = fmt::format("{}", dirent->inode); + attributes["brother120.start_sector"] = + fmt::format("{}", dirent->start_sector); + attributes["brother120.type"] = fmt::format("{}", dirent->brother_type); - return attributes; - } + return attributes; + } private: - std::vector readFat() - { - Bytes bytes = getLogicalSector(FAT_START_SECTOR, FAT_SECTORS); - ByteReader br(bytes); - std::vector table; + std::vector readFat() + { + Bytes bytes = getLogicalSector(FAT_START_SECTOR, FAT_SECTORS); + ByteReader br(bytes); + std::vector table; - table.push_back(0xffff); - for (int sector = 1; sector != SECTOR_COUNT; sector++) - table.push_back(br.read_be16()); + table.push_back(0xffff); + for (int sector = 1; sector != SECTOR_COUNT; sector++) + table.push_back(br.read_be16()); - return table; - } + return table; + } std::vector> findAllFiles() { - std::vector> result; - int inode = 0; - for (int block = 0; block < DIRECTORY_SECTORS; block++) - { - auto bytes = getLogicalSector(block); - for (int d = 0; d < SECTOR_SIZE/16; d++, inode++) - { - Bytes buffer = bytes.slice(d*16, 16); - if (buffer[0] == 0xf0) - continue; + std::vector> result; + int inode = 0; + for (int block = 0; block < DIRECTORY_SECTORS; block++) + { + auto bytes = getLogicalSector(block); + for (int d = 0; d < SECTOR_SIZE / 16; d++, inode++) + { + Bytes buffer = bytes.slice(d * 16, 16); + if (buffer[0] == 0xf0) + continue; - result.push_back( - std::make_unique(inode, buffer)); - } - } + result.push_back( + std::make_unique(inode, buffer)); + } + } return result; } - std::unique_ptr findFile(const Path& path) - { - if (path.size() != 1) - throw BadPathException(); + std::unique_ptr findFile(const Path& path) + { + if (path.size() != 1) + throw BadPathException(); - for (auto& dirent : findAllFiles()) + for (auto& dirent : findAllFiles()) { - if (dirent->filename == path[0]) - return std::move(dirent); + if (dirent->filename == path[0]) + return std::move(dirent); } - throw FileNotFoundException(); - } + throw FileNotFoundException(); + } private: const Brother120FsProto& _config; diff --git a/lib/vfs/cbmfs.cc b/lib/vfs/cbmfs.cc index 363cb964..c6fbcc13 100644 --- a/lib/vfs/cbmfs.cc +++ b/lib/vfs/cbmfs.cc @@ -6,11 +6,11 @@ enum { - DEL, - SEQ, - PRG, - USR, - REL + DEL, + SEQ, + PRG, + USR, + REL }; static std::string fromPetscii(const Bytes& bytes) @@ -30,25 +30,31 @@ static std::string fromPetscii(const Bytes& bytes) static std::string toMode(uint8_t cbm_type) { - std::stringstream ss; - if (cbm_type & 0x40) - ss << 'L'; - if (cbm_type & 0x80) - ss << 'S'; - return ss.str(); + std::stringstream ss; + if (cbm_type & 0x40) + ss << 'L'; + if (cbm_type & 0x80) + ss << 'S'; + return ss.str(); } static std::string toFileType(uint8_t cbm_type) { - switch (cbm_type & 0x0f) - { - case DEL: return "DEL"; - case SEQ: return "SEQ"; - case PRG: return "PRG"; - case USR: return "USR"; - case REL: return "REL"; - default: return fmt::format("[bad type {:x}]", cbm_type & 0x0f); - } + switch (cbm_type & 0x0f) + { + case DEL: + return "DEL"; + case SEQ: + return "SEQ"; + case PRG: + return "PRG"; + case USR: + return "USR"; + case REL: + return "REL"; + default: + return fmt::format("[bad type {:x}]", cbm_type & 0x0f); + } } class CbmfsDirent : public Dirent @@ -63,7 +69,7 @@ public: start_track = br.read_8(); start_sector = br.read_8(); - auto filenameBytes = br.read(16).split(0xa0)[0]; + auto filenameBytes = br.read(16).split(0xa0)[0]; filename = fromPetscii(filenameBytes); side_track = br.read_8(); side_sector = br.read_8(); @@ -103,8 +109,8 @@ public: std::vector> list(const Path& path) { - if (path.size() != 0) - throw BadPathException(); + if (path.size() != 0) + throw BadPathException(); std::vector> results; uint8_t t = _config.directory_track(); @@ -131,64 +137,64 @@ public: std::map getMetadata(const Path& path) { - if (path.size() != 1) - throw BadPathException(); - auto de = findFile(unhex(path[0])); - if (!de) - throw FileNotFoundException(); - - std::map attributes; - attributes["filename"] = de->filename; - attributes["length"] = fmt::format("{}", de->length); - attributes["mode"] = de->mode; - attributes["type"] = "file"; - attributes["cbmfs.type"] = toFileType(de->cbm_type); - attributes["cbmfs.start_track"] = fmt::format("{}", de->start_track); - attributes["cbmfs.start_sector"] = fmt::format("{}", de->start_sector); - attributes["cbmfs.side_track"] = fmt::format("{}", de->side_track); - attributes["cbmfs.side_sector"] = fmt::format("{}", de->side_sector); - attributes["cbmfs.recordlen"] = fmt::format("{}", de->recordlen); - attributes["cbmfs.sectors"] = fmt::format("{}", de->sectors); - return attributes; - } - - Bytes getFile(const Path& path) - { if (path.size() != 1) throw BadPathException(); - auto de = findFile(unhex(path[0])); - if (!de) - throw FileNotFoundException(); - if (de->cbm_type == REL) - throw UnimplementedFilesystemException("cannot read .REL files"); + auto de = findFile(unhex(path[0])); + if (!de) + throw FileNotFoundException(); - Bytes bytes; - ByteWriter bw(bytes); + std::map attributes; + attributes["filename"] = de->filename; + attributes["length"] = fmt::format("{}", de->length); + attributes["mode"] = de->mode; + attributes["type"] = "file"; + attributes["cbmfs.type"] = toFileType(de->cbm_type); + attributes["cbmfs.start_track"] = fmt::format("{}", de->start_track); + attributes["cbmfs.start_sector"] = fmt::format("{}", de->start_sector); + attributes["cbmfs.side_track"] = fmt::format("{}", de->side_track); + attributes["cbmfs.side_sector"] = fmt::format("{}", de->side_sector); + attributes["cbmfs.recordlen"] = fmt::format("{}", de->recordlen); + attributes["cbmfs.sectors"] = fmt::format("{}", de->sectors); + return attributes; + } + + Bytes getFile(const Path& path) + { + if (path.size() != 1) + throw BadPathException(); + auto de = findFile(unhex(path[0])); + if (!de) + throw FileNotFoundException(); + if (de->cbm_type == REL) + throw UnimplementedFilesystemException("cannot read .REL files"); + + Bytes bytes; + ByteWriter bw(bytes); uint8_t t = de->start_track - 1; uint8_t s = de->start_sector; - for (;;) + for (;;) { auto b = getSector(t, 0, s); - if (b[0]) - bw += b.slice(2); - else - { - bw += b.slice(2, b[1]); - break; - } + if (b[0]) + bw += b.slice(2); + else + { + bw += b.slice(2, b[1]); + break; + } t = b[0] - 1; s = b[1]; } - return bytes; - } + return bytes; + } private: - std::unique_ptr findFile(const std::string& filename) - { + std::unique_ptr findFile(const std::string& filename) + { uint8_t t = _config.directory_track(); uint8_t s = 1; while (t != 0xff) @@ -201,17 +207,17 @@ private: if (dbuf[2] == 0) continue; - auto de = std::make_unique(dbuf); - if (de->filename == filename) - return de; + auto de = std::make_unique(dbuf); + if (de->filename == filename) + return de; } t = b[0] - 1; s = b[1]; } - return nullptr; - } + return nullptr; + } private: const CbmfsProto& _config; diff --git a/lib/vfs/fatfs.cc b/lib/vfs/fatfs.cc index 2a2c57dd..81169669 100644 --- a/lib/vfs/fatfs.cc +++ b/lib/vfs/fatfs.cc @@ -116,6 +116,30 @@ public: return bytes; } + void putFile(const Path& path, const Bytes& bytes) + { + mount(); + auto pathstr = path.to_str(); + FIL fil; + FRESULT res = + f_open(&fil, pathstr.c_str(), FA_WRITE | FA_CREATE_ALWAYS); + throwError(res); + + unsigned remaining = bytes.size(); + char* ptr = (char*)bytes.cbegin(); + while (remaining != 0) + { + UINT done; + res = f_write(&fil, ptr, remaining, &done); + throwError(res); + + remaining -= done; + ptr += done; + } + + f_close(&fil); + } + public: DRESULT diskRead(BYTE* buffer, LBA_t sector, UINT count) { @@ -126,7 +150,9 @@ public: DRESULT diskWrite(const BYTE* buffer, LBA_t sector, UINT count) { - return RES_WRPRT; + Bytes bytes(buffer, count * getLogicalSectorSize()); + putLogicalSector(sector, bytes); + return RES_OK; } DRESULT diskIoctl(BYTE cmd, void* buffer) @@ -163,6 +189,16 @@ private: case FR_NO_PATH: throw FileNotFoundException(); + case FR_INVALID_NAME: + throw BadPathException(); + + case FR_DENIED: + case FR_EXIST: + case FR_WRITE_PROTECTED: + case FR_MKFS_ABORTED: + case FR_LOCKED: + throw CannotWriteException(); + case FR_NO_FILESYSTEM: throw BadFilesystemException(); diff --git a/lib/vfs/fluxsectorinterface.cc b/lib/vfs/fluxsectorinterface.cc index 66a0ef6d..2a5e5553 100644 --- a/lib/vfs/fluxsectorinterface.cc +++ b/lib/vfs/fluxsectorinterface.cc @@ -4,14 +4,20 @@ #include "lib/readerwriter.h" #include "lib/decoders/decoders.h" #include "lib/fluxsource/fluxsource.h" +#include "lib/layout.h" +#include "lib/proto.h" #include "lib/mapper.h" class FluxSectorInterface : public SectorInterface { public: FluxSectorInterface(std::shared_ptr fluxSource, + std::shared_ptr fluxSink, + std::shared_ptr encoder, std::shared_ptr decoder): _fluxSource(fluxSource), + _fluxSink(fluxSink), + _encoder(encoder), _decoder(decoder) { } @@ -20,47 +26,124 @@ public: std::shared_ptr get( unsigned track, unsigned side, unsigned sectorId) { + auto it = _changedSectors.get(track, side, sectorId); + if (it) + return it; + trackid_t trackid(track, side); if (_loadedtracks.find(trackid) == _loadedtracks.end()) - populateSectors(track, side); + populateSectors(track, side); - return _sectorstore.get(track, side, sectorId); + return _readSectors.get(track, side, sectorId); } std::shared_ptr put( unsigned track, unsigned side, unsigned sectorId) { trackid_t trackid(track, side); - if (_loadedtracks.find(trackid) == _loadedtracks.end()) - populateSectors(track, side); + _changedtracks.insert(trackid); + return _changedSectors.put(track, side, sectorId); + } - _changedtracks.insert(trackid); - return _sectorstore.put(track, side, sectorId); + void flush() + { + for (const auto& trackid : _changedtracks) + { + unsigned track = trackid.first; + unsigned side = trackid.second; + auto layoutdata = Layout::getLayoutOfTrack(track, side); + auto sectors = Layout::getSectorsInTrack(layoutdata); + + config.mutable_tracks()->Clear(); + config.mutable_tracks()->set_start(track); + + config.mutable_heads()->Clear(); + config.mutable_heads()->set_start(side); + + /* Check to see if we have all sectors of this track in the + * changesectors image. */ + + if (imageContainsAllSectorsOf( + _changedSectors, track, side, sectors)) + { + /* Just write directly from the changedsectors image. */ + + writeDiskCommand(_changedSectors, + *_encoder, + *_fluxSink, + &*_decoder, + &*_fluxSource); + } + else + { + /* Only a few sectors have changed. Do we need to populate the track? */ + + if (_loadedtracks.find(trackid) == _loadedtracks.end()) + populateSectors(track, side); + + /* Now merge the loaded track with the changed one, and write the result back. */ + + Image image; + for (const unsigned sector : sectors) + { + auto s = image.put(track, side, sector); + if (_changedSectors.contains(track, side, sector)) + s->data = _changedSectors.get(track, side, sector)->data; + else + s->data = _readSectors.get(track, side, sector)->data; + } + + writeDiskCommand(image, + *_encoder, + *_fluxSink, + &*_decoder, + &*_fluxSource); + } + } } private: + bool imageContainsAllSectorsOf(const Image& image, + unsigned track, + unsigned side, + const std::vector& sectors) + { + for (unsigned sector : sectors) + { + if (!image.contains(track, side, sector)) + return false; + } + return true; + } + void populateSectors(unsigned track, unsigned side) { - auto location = Mapper::computeLocationFor(track, side); - auto trackdata = - readAndDecodeTrack(*_fluxSource, *_decoder, location); + auto location = Mapper::computeLocationFor(track, side); + auto trackdata = readAndDecodeTrack(*_fluxSource, *_decoder, location); - for (const auto& sector : trackdata->sectors) - *_sectorstore.put(track, side, sector->logicalSector) = *sector; - _loadedtracks.insert(trackid_t(track, side)); + for (const auto& sector : trackdata->sectors) + *_readSectors.put(track, side, sector->logicalSector) = *sector; + _loadedtracks.insert(trackid_t(track, side)); } std::shared_ptr _fluxSource; + std::shared_ptr _fluxSink; + std::shared_ptr _encoder; std::shared_ptr _decoder; typedef std::pair trackid_t; - Image _sectorstore; + Image _readSectors; + Image _changedSectors; std::set _loadedtracks; std::set _changedtracks; }; std::unique_ptr SectorInterface::createFluxSectorInterface( - std::shared_ptr fluxSource, std::shared_ptr decoder) + std::shared_ptr fluxSource, + std::shared_ptr fluxSink, + std::shared_ptr encoder, + std::shared_ptr decoder) { - return std::make_unique(fluxSource, decoder); + return std::make_unique( + fluxSource, fluxSink, encoder, decoder); } diff --git a/lib/vfs/imagesectorinterface.cc b/lib/vfs/imagesectorinterface.cc index d4408f3d..a05f7e79 100644 --- a/lib/vfs/imagesectorinterface.cc +++ b/lib/vfs/imagesectorinterface.cc @@ -5,27 +5,27 @@ class ImageSectorInterface : public SectorInterface { public: - ImageSectorInterface(std::shared_ptr image): - _image(image) - {} + ImageSectorInterface(std::shared_ptr image): _image(image) {} public: - std::shared_ptr get(unsigned track, unsigned side, unsigned sectorId) - { - return _image->get(track, side, sectorId); - } + std::shared_ptr get( + unsigned track, unsigned side, unsigned sectorId) + { + return _image->get(track, side, sectorId); + } - std::shared_ptr put(unsigned track, unsigned side, unsigned sectorId) - { - return _image->put(track, side, sectorId); - } + std::shared_ptr put( + unsigned track, unsigned side, unsigned sectorId) + { + return _image->put(track, side, sectorId); + } private: - std::shared_ptr _image; + std::shared_ptr _image; }; -std::unique_ptr SectorInterface::createImageSectorInterface(std::shared_ptr image) +std::unique_ptr SectorInterface::createImageSectorInterface( + std::shared_ptr image) { - return std::make_unique(image); + return std::make_unique(image); } - diff --git a/lib/vfs/machfs.cc b/lib/vfs/machfs.cc index aa6832b4..7987fdb3 100644 --- a/lib/vfs/machfs.cc +++ b/lib/vfs/machfs.cc @@ -130,8 +130,8 @@ public: hfsdirent de; hfs_fstat(file, &de); - a.creator = Bytes(de.u.file.creator); - a.type = Bytes(de.u.file.type); + a.creator = Bytes(de.u.file.creator); + a.type = Bytes(de.u.file.type); hfs_setfork(file, 0); a.data = readBytes(file); @@ -183,23 +183,37 @@ private: class HfsFile { public: - HfsFile(hfsfile* file): _file(file) {} - ~HfsFile() { hfs_close(_file); } + HfsFile(hfsfile* file): _file(file) {} + ~HfsFile() + { + hfs_close(_file); + } + + operator hfsfile*() const + { + return _file; + } - operator hfsfile* () const { return _file; } private: - hfsfile* _file; + hfsfile* _file; }; class HfsDir { public: - HfsDir(hfsdir* dir): _dir(dir) {} - ~HfsDir() { hfs_closedir(_dir); } + HfsDir(hfsdir* dir): _dir(dir) {} + ~HfsDir() + { + hfs_closedir(_dir); + } + + operator hfsdir*() const + { + return _dir; + } - operator hfsdir* () const { return _dir; } private: - hfsdir* _dir; + hfsdir* _dir; }; private: diff --git a/lib/vfs/sectorinterface.h b/lib/vfs/sectorinterface.h index 7b4f4165..a3fdcc8c 100644 --- a/lib/vfs/sectorinterface.h +++ b/lib/vfs/sectorinterface.h @@ -4,7 +4,9 @@ class Image; class Sector; class FluxSource; +class FluxSink; class AbstractDecoder; +class AbstractEncoder; class SectorInterface { @@ -21,6 +23,8 @@ public: std::shared_ptr image); static std::unique_ptr createFluxSectorInterface( std::shared_ptr fluxSource, + std::shared_ptr fluxSink, + std::shared_ptr encoder, std::shared_ptr decoder); }; diff --git a/lib/vfs/vfs.cc b/lib/vfs/vfs.cc index 34fa7924..a501c6c3 100644 --- a/lib/vfs/vfs.cc +++ b/lib/vfs/vfs.cc @@ -33,6 +33,57 @@ std::string Path::to_str(const std::string sep) const return join(*this, sep); } +void Filesystem::create() +{ + throw UnimplementedFilesystemException(); +} + +FilesystemStatus Filesystem::check() +{ + throw UnimplementedFilesystemException(); +} + +std::vector> Filesystem::list(const Path& path) +{ + throw UnimplementedFilesystemException(); +} + +Bytes Filesystem::getFile(const Path& path) +{ + throw UnimplementedFilesystemException(); +} + +void Filesystem::putFile(const Path& path, const Bytes& data) +{ + throw UnimplementedFilesystemException(); +} + +std::map Filesystem::getMetadata(const Path& path) +{ + throw UnimplementedFilesystemException(); +} + +void Filesystem::putMetadata( + const Path& path, const std::map& metadata) +{ + throw UnimplementedFilesystemException(); +} + +void Filesystem::createDirectory(const Path& path) +{ + throw UnimplementedFilesystemException(); +} + +void Filesystem::deleteFile(const Path& path) +{ + throw UnimplementedFilesystemException(); +} + +void Filesystem::flush() +{ + _sectors->flush(); +} + Filesystem::Filesystem(std::shared_ptr sectors): _sectors(sectors) { @@ -125,8 +176,19 @@ void Filesystem::putLogicalSector(uint32_t number, const Bytes& data) if (number >= _locations.size()) throw BadFilesystemException(); - auto& i = _locations[number]; - _sectors->put(std::get<0>(i), std::get<1>(i), std::get<2>(i))->data = data; + unsigned pos = 0; + while (pos < data.size()) + { + auto& it = _locations[number]; + int track = std::get<0>(it); + int side = std::get<1>(it); + int sector = std::get<2>(it); + int sectorSize = Layout::getLayoutOfTrack(track, side).sector_size(); + + _sectors->put(track, side, sector)->data = data.slice(pos, sectorSize); + pos += sectorSize; + number++; + } } unsigned Filesystem::getOffsetOfSector( diff --git a/lib/vfs/vfs.h b/lib/vfs/vfs.h index 8c31e958..ab5b75e3 100644 --- a/lib/vfs/vfs.h +++ b/lib/vfs/vfs.h @@ -60,6 +60,12 @@ public: BadFilesystemException(): FilesystemException("Invalid filesystem") {} }; +class CannotWriteException : public FilesystemException +{ +public: + CannotWriteException(): FilesystemException("Cannot write file") {} +}; + class ReadOnlyFilesystemException : public FilesystemException { public: @@ -95,51 +101,17 @@ public: class Filesystem { public: - virtual void create() - { - throw UnimplementedFilesystemException(); - } - - virtual FilesystemStatus check() - { - throw UnimplementedFilesystemException(); - } - - virtual std::vector> list(const Path& path) - { - throw UnimplementedFilesystemException(); - } - - virtual Bytes getFile(const Path& path) - { - throw UnimplementedFilesystemException(); - } - - virtual void putFile(const Path& path, const Bytes& data) - { - throw UnimplementedFilesystemException(); - } - - virtual std::map getMetadata(const Path& path) - { - throw UnimplementedFilesystemException(); - } - + virtual void create(); + virtual FilesystemStatus check(); + virtual std::vector> list(const Path& path); + virtual Bytes getFile(const Path& path); + virtual void putFile(const Path& path, const Bytes& data); + virtual std::map getMetadata(const Path& path); virtual void putMetadata( - const Path& path, const std::map& metadata) - { - throw UnimplementedFilesystemException(); - } - - virtual void createDirectory(const Path& path) - { - throw UnimplementedFilesystemException(); - } - - virtual void deleteFile(const Path& path) - { - throw UnimplementedFilesystemException(); - } + const Path& path, const std::map& metadata); + virtual void createDirectory(const Path& path); + virtual void deleteFile(const Path& path); + void flush(); protected: Filesystem(std::shared_ptr sectors); @@ -149,7 +121,7 @@ protected: Bytes getLogicalSector(uint32_t number, uint32_t count = 1); void putLogicalSector(uint32_t number, const Bytes& data); - unsigned getOffsetOfSector(unsigned track, unsigned side, unsigned sector); + unsigned getOffsetOfSector(unsigned track, unsigned side, unsigned sector); unsigned getLogicalSectorCount(); unsigned getLogicalSectorSize(unsigned track = 0, unsigned side = 0); diff --git a/lib/vfs/vfs.proto b/lib/vfs/vfs.proto index 48b2b15a..d54d802b 100644 --- a/lib/vfs/vfs.proto +++ b/lib/vfs/vfs.proto @@ -2,60 +2,70 @@ syntax = "proto2"; import "lib/common.proto"; -message AcornDfsProto { - enum Flavour { - UNDEFINED = 0; - ACORN_DFS = 1; - } +message AcornDfsProto +{ + enum Flavour + { + UNDEFINED = 0; + ACORN_DFS = 1; + } - optional Flavour flavour = 1 [default = ACORN_DFS, (help) = "which flavour of DFS to implement"]; + optional Flavour flavour = 1 + [ default = ACORN_DFS, (help) = "which flavour of DFS to implement" ]; } -message Brother120FsProto { +message Brother120FsProto {} + +message FatFsProto {} + +message CpmFsProto +{ + message Location + { + optional uint32 track = 1 [ (help) = "track number" ]; + optional uint32 side = 2 [ (help) = "side number" ]; + optional uint32 sector = 3 [ (help) = "sector ID" ]; + } + + message Padding + { + optional uint32 amount = 1 + [ (help) = "number of sectors of padding to insert" ]; + optional uint32 every = 2 + [ (help) = "insert padding after this many sectors" ]; + } + + optional Location filesystem_start = 1 + [ (help) = "position of the start of the filesystem" ]; + optional int32 block_size = 2 [ (help) = "allocation block size" ]; + optional int32 dir_entries = 3 + [ (help) = "number of entries in the directory" ]; + optional Padding padding = 4 + [ (help) = "wasted sectors not considered part of the filesystem" ]; } -message FatFsProto { +message AmigaFfsProto {} + +message MacHfsProto {} + +message CbmfsProto +{ + optional uint32 directory_track = 1 [ + default = 17, + (help) = "which track the directory is on (zero-based numbering)" + ]; } -message CpmFsProto { - message Location { - optional uint32 track = 1 [(help) = "track number"]; - optional uint32 side = 2 [(help) = "side number"]; - optional uint32 sector = 3 [(help) = "sector ID"]; - } - - message Padding { - optional uint32 amount = 1 [(help) = "number of sectors of padding to insert"]; - optional uint32 every = 2 [(help) = "insert padding after this many sectors"]; - } - - optional Location filesystem_start = 1 [(help) = "position of the start of the filesystem"]; - optional int32 block_size = 2 [(help) = "allocation block size"]; - optional int32 dir_entries = 3 [(help) = "number of entries in the directory"]; - optional Padding padding = 4 [(help) = "wasted sectors not considered part of the filesystem"]; +message FilesystemProto +{ + oneof filesystem + { + AcornDfsProto acorndfs = 1; + Brother120FsProto brother120 = 2; + FatFsProto fatfs = 3; + CpmFsProto cpmfs = 4; + AmigaFfsProto amigaffs = 5; + MacHfsProto machfs = 6; + CbmfsProto cbmfs = 7; + } } - -message AmigaFfsProto { -} - -message MacHfsProto { -} - -message CbmfsProto { - optional uint32 directory_track = 1 - [default = 17, - (help) = "which track the directory is on (zero-based numbering)"]; -} - -message FilesystemProto { - oneof filesystem { - AcornDfsProto acorndfs = 1; - Brother120FsProto brother120 = 2; - FatFsProto fatfs = 3; - CpmFsProto cpmfs = 4; - AmigaFfsProto amigaffs = 5; - MacHfsProto machfs = 6; - CbmfsProto cbmfs = 7; - } -} - diff --git a/src/build.mk b/src/build.mk index 39eaeaa0..0f5559e5 100644 --- a/src/build.mk +++ b/src/build.mk @@ -7,6 +7,7 @@ FLUXENGINE_SRCS = \ src/fe-getfileinfo.cc \ src/fe-inspect.cc \ src/fe-ls.cc \ + src/fe-putfile.cc \ src/fe-rawread.cc \ src/fe-rawwrite.cc \ src/fe-read.cc \ diff --git a/src/fe-getfile.cc b/src/fe-getfile.cc index 363a9a24..d2e9012c 100644 --- a/src/fe-getfile.cc +++ b/src/fe-getfile.cc @@ -18,8 +18,8 @@ static FlagGroup flags({&fileFlags}); -static StringFlag directory({"-p", "--path"}, "path to work on", ""); -static StringFlag output({"-o", "--output"}, "local filename to write to", ""); +static StringFlag directory({"-p", "--path"}, "disk path to work on", ""); +static StringFlag output({"-l", "--local"}, "local filename to write to", ""); int mainGetFile(int argc, const char* argv[]) { diff --git a/src/fe-putfile.cc b/src/fe-putfile.cc new file mode 100644 index 00000000..49b34ac2 --- /dev/null +++ b/src/fe-putfile.cc @@ -0,0 +1,51 @@ +#include "globals.h" +#include "flags.h" +#include "fluxmap.h" +#include "sector.h" +#include "proto.h" +#include "readerwriter.h" +#include "imagereader/imagereader.h" +#include "imagewriter/imagewriter.h" +#include "lib/fluxsource/fluxsource.h" +#include "lib/decoders/decoders.h" +#include "fmt/format.h" +#include "fluxengine.h" +#include "lib/vfs/sectorinterface.h" +#include "lib/vfs/vfs.h" +#include "src/fileutils.h" +#include +#include + +static FlagGroup flags({&fileFlags}); + +static StringFlag path({"-p", "--path"}, "path to work on", ""); +static StringFlag input({"-l", "--local"}, "local filename to read from", ""); + +int mainPutFile(int argc, const char* argv[]) +{ + if (argc == 1) + showProfiles("putfile", formats); + flags.parseFlagsWithConfigFiles(argc, argv, formats); + + try + { + std::string inputFilename = input; + if (inputFilename.empty()) + Error() << "you must supply a local file to read from"; + + Path outputFilename(path); + if (outputFilename.size() == 0) + Error() << "you must supply a destination path to write to"; + + auto data = Bytes::readFromFile(inputFilename); + auto filesystem = createFilesystemFromConfig(); + filesystem->putFile(outputFilename, data); + filesystem->flush(); + } + catch (const FilesystemException& e) + { + Error() << e.message; + } + + return 0; +} diff --git a/src/fe-write.cc b/src/fe-write.cc index a0317fd1..e4222ffb 100644 --- a/src/fe-write.cc +++ b/src/fe-write.cc @@ -86,7 +86,7 @@ int mainWrite(int argc, const char* argv[]) if (verify && config.has_flux_source() && config.flux_source().has_drive()) fluxSource = FluxSource::create(config.flux_source()); - writeDiskCommand(image, *encoder, *fluxSink, decoder.get(), fluxSource.get()); + writeDiskCommand(*image, *encoder, *fluxSink, decoder.get(), fluxSource.get()); return 0; } diff --git a/src/fileutils.cc b/src/fileutils.cc index 28e0017c..049c6299 100644 --- a/src/fileutils.cc +++ b/src/fileutils.cc @@ -5,7 +5,9 @@ #include "proto.h" #include "readerwriter.h" #include "lib/decoders/decoders.h" +#include "lib/encoders/encoders.h" #include "lib/fluxsource/fluxsource.h" +#include "lib/fluxsink/fluxsink.h" #include "lib/imagereader/imagereader.h" #include "fmt/format.h" #include "fluxengine.h" @@ -32,6 +34,8 @@ static StringFlag flux({"-f", "--flux"}, { FluxSource::updateConfigForFilename( config.mutable_flux_source(), value); + FluxSink::updateConfigForFilename( + config.mutable_flux_sink(), value); }); std::unique_ptr createFilesystemFromConfig() @@ -40,8 +44,10 @@ std::unique_ptr createFilesystemFromConfig() if (config.has_flux_source()) { std::shared_ptr fluxSource(FluxSource::create(config.flux_source())); + std::shared_ptr fluxSink(FluxSink::create(config.flux_sink())); + std::shared_ptr encoder(AbstractEncoder::create(config.encoder())); std::shared_ptr decoder(AbstractDecoder::create(config.decoder())); - sectorInterface = SectorInterface::createFluxSectorInterface(fluxSource, decoder); + sectorInterface = SectorInterface::createFluxSectorInterface(fluxSource, fluxSink, encoder, decoder); } else { diff --git a/src/fluxengine.cc b/src/fluxengine.cc index 871dce7a..a1dd16e2 100644 --- a/src/fluxengine.cc +++ b/src/fluxengine.cc @@ -10,6 +10,7 @@ extern command_cb mainGetFile; extern command_cb mainGetFileInfo; extern command_cb mainInspect; extern command_cb mainLs; +extern command_cb mainPutFile; extern command_cb mainRawRead; extern command_cb mainRawWrite; extern command_cb mainRead; @@ -40,6 +41,7 @@ static std::vector commands = { "ls", mainLs, "Show files on disk (or image).", }, { "getfile", mainGetFile, "Read a file off a disk (or image).", }, { "getfileinfo", mainGetFileInfo, "Read file metadata off a disk (or image).", }, + { "putfile", mainPutFile, "Write a file to disk (or image).", }, { "rpm", mainRpm, "Measures the disk rotational speed.", }, { "seek", mainSeek, "Moves the disk head.", }, { "test", mainTest, "Various testing commands.", }, diff --git a/src/gui/mainwindow.cc b/src/gui/mainwindow.cc index 6459428c..9560f230 100644 --- a/src/gui/mainwindow.cc +++ b/src/gui/mainwindow.cc @@ -157,7 +157,7 @@ void MainWindow::OnWriteFluxButton(wxCommandEvent&) decoder = AbstractDecoder::create(config.decoder()); fluxSource = FluxSource::create(config.flux_source()); } - writeDiskCommand(image, *encoder, *fluxSink, decoder.get(), fluxSource.get()); + writeDiskCommand(*image, *encoder, *fluxSink, decoder.get(), fluxSource.get()); }); } catch (const ErrorException& e) From f382b70cdfd8310ba25929e8643d26d10d96f88f Mon Sep 17 00:00:00 2001 From: David Given Date: Tue, 30 Aug 2022 23:13:30 +0200 Subject: [PATCH 2/4] Fix verification bug with multiple sector sizes. --- lib/readerwriter.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/readerwriter.cc b/lib/readerwriter.cc index d14c0833..54939fb4 100644 --- a/lib/readerwriter.cc +++ b/lib/readerwriter.cc @@ -297,7 +297,7 @@ void writeTracksAndVerify(FluxSink& fluxSink, Logger() << "spurious sector on verify"; return false; } - if (s->data != sector->data) + if (s->data != sector->data.slice(0, s->data.size())) { Logger() << "data mismatch on verify"; return false; From 42a350156a345d997092a0ef5bf9a4004e8312f6 Mon Sep 17 00:00:00 2001 From: David Given Date: Tue, 30 Aug 2022 23:13:59 +0200 Subject: [PATCH 3/4] Amiga FFS writes work. --- lib/vfs/amigaffs.cc | 28 +++++++++++++++++++++++- lib/vfs/fluxsectorinterface.cc | 40 +++++++++++++++++----------------- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/lib/vfs/amigaffs.cc b/lib/vfs/amigaffs.cc index ac8297e8..1cf8fca8 100644 --- a/lib/vfs/amigaffs.cc +++ b/lib/vfs/amigaffs.cc @@ -147,6 +147,30 @@ public: return bytes; } + void putFile(const Path& path, const Bytes& data) + { + AdfMount m(this); + if (path.size() == 0) + throw BadPathException(); + + auto* vol = m.getVolume(); + changeDirButOne(vol, path); + + auto* file = adfOpenFile(vol, (char*)path.back().c_str(), (char*)"w"); + if (!file) + throw CannotWriteException(); + + unsigned pos = 0; + while (pos != data.size()) + { + long done = adfWriteFile( + file, data.size() - pos, (uint8_t*)data.cbegin() + pos); + pos += done; + } + + adfCloseFile(file); + } + private: class AdfEntry { @@ -270,7 +294,9 @@ public: RETCODE adfNativeWriteSector( struct Device* dev, int32_t sector, int size, uint8_t* buffer) { - return RC_OK; + Bytes bytes(buffer, size); + putLogicalSector(sector, bytes); + return RC_OK; } private: diff --git a/lib/vfs/fluxsectorinterface.cc b/lib/vfs/fluxsectorinterface.cc index 2a5e5553..880df2c5 100644 --- a/lib/vfs/fluxsectorinterface.cc +++ b/lib/vfs/fluxsectorinterface.cc @@ -75,30 +75,30 @@ public: &*_fluxSource); } else - { - /* Only a few sectors have changed. Do we need to populate the track? */ + { + /* Only a few sectors have changed. Do we need to populate the + * track? */ - if (_loadedtracks.find(trackid) == _loadedtracks.end()) - populateSectors(track, side); + if (_loadedtracks.find(trackid) == _loadedtracks.end()) + populateSectors(track, side); - /* Now merge the loaded track with the changed one, and write the result back. */ + /* Now merge the loaded track with the changed one, and write + * the result back. */ - Image image; - for (const unsigned sector : sectors) - { - auto s = image.put(track, side, sector); - if (_changedSectors.contains(track, side, sector)) - s->data = _changedSectors.get(track, side, sector)->data; - else - s->data = _readSectors.get(track, side, sector)->data; - } + Image image; + for (const unsigned sector : sectors) + { + auto s = image.put(track, side, sector); + if (_changedSectors.contains(track, side, sector)) + s->data = + _changedSectors.get(track, side, sector)->data; + else + s->data = _readSectors.get(track, side, sector)->data; + } - writeDiskCommand(image, - *_encoder, - *_fluxSink, - &*_decoder, - &*_fluxSource); - } + writeDiskCommand( + image, *_encoder, *_fluxSink, &*_decoder, &*_fluxSource); + } } } From b7eee599f4cd500191c7e1f7ed22be6bf1edbcc2 Mon Sep 17 00:00:00 2001 From: David Given Date: Tue, 30 Aug 2022 23:30:23 +0200 Subject: [PATCH 4/4] Mac HFS writing works. --- lib/vfs/amigaffs.cc | 6 ++--- lib/vfs/machfs.cc | 54 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/lib/vfs/amigaffs.cc b/lib/vfs/amigaffs.cc index 1cf8fca8..a6ca589c 100644 --- a/lib/vfs/amigaffs.cc +++ b/lib/vfs/amigaffs.cc @@ -294,9 +294,9 @@ public: RETCODE adfNativeWriteSector( struct Device* dev, int32_t sector, int size, uint8_t* buffer) { - Bytes bytes(buffer, size); - putLogicalSector(sector, bytes); - return RC_OK; + Bytes bytes(buffer, size); + putLogicalSector(sector, bytes); + return RC_OK; } private: diff --git a/lib/vfs/machfs.cc b/lib/vfs/machfs.cc index 7987fdb3..3b7d7afb 100644 --- a/lib/vfs/machfs.cc +++ b/lib/vfs/machfs.cc @@ -141,6 +141,38 @@ public: return a.render(); } + void putFile(const Path& path, const Bytes& bytes) + { + HfsMount m(this); + if (path.size() == 0) + throw BadPathException(); + + AppleSingle a; + try + { + a.parse(bytes); + } + catch (const InvalidFileException& e) + { + throw UnimplementedFilesystemException( + "you can only write valid AppleSingle encoded files"); + } + + auto pathstr = ":" + path.to_str(":"); + hfs_delete(_vol, pathstr.c_str()); + HfsFile file(hfs_create(_vol, + pathstr.c_str(), + (const char*)a.type.cbegin(), + (const char*)a.creator.cbegin())); + if (!file) + throw CannotWriteException(); + + hfs_setfork(file, 0); + writeBytes(file, a.data); + hfs_setfork(file, 1); + writeBytes(file, a.rsrc); + } + private: Bytes readBytes(hfsfile* file) { @@ -161,6 +193,17 @@ private: return bytes; } + void writeBytes(hfsfile* file, const Bytes& bytes) + { + unsigned pos = 0; + while (pos != bytes.size()) + { + unsigned long done = + hfs_write(file, bytes.cbegin() + pos, bytes.size() - pos); + pos += done; + } + } + private: class HfsMount { @@ -186,7 +229,8 @@ private: HfsFile(hfsfile* file): _file(file) {} ~HfsFile() { - hfs_close(_file); + if (_file) + hfs_close(_file); } operator hfsfile*() const @@ -204,7 +248,8 @@ private: HfsDir(hfsdir* dir): _dir(dir) {} ~HfsDir() { - hfs_closedir(_dir); + if (_dir) + hfs_closedir(_dir); } operator hfsdir*() const @@ -241,7 +286,10 @@ private: void** priv, const void* buffer, unsigned long len); unsigned long hfsWrite(const void* buffer, unsigned long len) { - return -1; + Bytes bytes((const uint8_t*)buffer, len * 512); + putLogicalSector(_seek, bytes); + _seek += len; + return len; } private: