mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
13
lib/bytes.cc
13
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;
|
||||
|
||||
@@ -19,6 +19,9 @@ public:
|
||||
|
||||
Bytes* operator = (const Bytes& other);
|
||||
|
||||
public:
|
||||
static Bytes readFromFile(const std::string& filename);
|
||||
|
||||
public:
|
||||
/* General purpose methods */
|
||||
|
||||
|
||||
17
lib/image.cc
17
lib/image.cc
@@ -15,6 +15,17 @@ Image::Image(std::set<std::shared_ptr<const Sector>>& 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<const Sector> Image::get(unsigned track, unsigned side, unsigned sectorid) const
|
||||
{
|
||||
static std::shared_ptr<const Sector> NONE;
|
||||
@@ -37,6 +48,12 @@ std::shared_ptr<Sector> 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 = {};
|
||||
|
||||
@@ -39,8 +39,11 @@ public:
|
||||
public:
|
||||
void calculateSize();
|
||||
|
||||
bool empty() const;
|
||||
bool contains(unsigned track, unsigned side, unsigned sectorId) const;
|
||||
std::shared_ptr<const Sector> get(unsigned track, unsigned side, unsigned sectorId) const;
|
||||
std::shared_ptr<Sector> 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()); }
|
||||
|
||||
@@ -187,8 +187,6 @@ void writeTracks(FluxSink& fluxSink,
|
||||
producer,
|
||||
std::function<bool(const Location& location)> 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<std::shared_ptr<const Sector>> 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))
|
||||
for (const auto& sector : trackFlux->sectors)
|
||||
{
|
||||
Logger() << "good read but the data doesn't match";
|
||||
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.slice(0, s->data.size()))
|
||||
{
|
||||
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<const Image> image,
|
||||
void writeDiskCommand(const Image& image,
|
||||
AbstractEncoder& encoder,
|
||||
FluxSink& fluxSink,
|
||||
AbstractDecoder* decoder,
|
||||
FluxSource* fluxSource)
|
||||
{
|
||||
const Image* imagep = ℑ
|
||||
std::unique_ptr<const Image> 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)
|
||||
|
||||
@@ -23,7 +23,7 @@ extern void fillBitmapTo(std::vector<bool>& bitmap,
|
||||
unsigned terminateAt,
|
||||
const std::vector<bool>& pattern);
|
||||
|
||||
extern void writeDiskCommand(std::shared_ptr<const Image> image,
|
||||
extern void writeDiskCommand(const Image& image,
|
||||
AbstractEncoder& encoder,
|
||||
FluxSink& fluxSink,
|
||||
AbstractDecoder* decoder = nullptr,
|
||||
|
||||
@@ -16,8 +16,10 @@ public:
|
||||
|
||||
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];
|
||||
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;
|
||||
@@ -86,9 +88,12 @@ public:
|
||||
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.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;
|
||||
@@ -110,7 +115,8 @@ private:
|
||||
auto bytes0 = sector0.slice(i * 8 + 8, 8);
|
||||
auto bytes1 = sector1.slice(i * 8 + 8, 8);
|
||||
|
||||
result.push_back(std::make_unique<AcornDfsDirent>(i, bytes0, bytes1));
|
||||
result.push_back(
|
||||
std::make_unique<AcornDfsDirent>(i, bytes0, bytes1));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -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,6 +294,8 @@ public:
|
||||
RETCODE adfNativeWriteSector(
|
||||
struct Device* dev, int32_t sector, int size, uint8_t* buffer)
|
||||
{
|
||||
Bytes bytes(buffer, size);
|
||||
putLogicalSector(sector, bytes);
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ public:
|
||||
class Brother120Filesystem : public Filesystem
|
||||
{
|
||||
public:
|
||||
Brother120Filesystem(
|
||||
const Brother120FsProto& config, std::shared_ptr<SectorInterface> sectors):
|
||||
Brother120Filesystem(const Brother120FsProto& config,
|
||||
std::shared_ptr<SectorInterface> sectors):
|
||||
Filesystem(sectors),
|
||||
_config(config)
|
||||
{
|
||||
@@ -101,7 +101,8 @@ public:
|
||||
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.start_sector"] =
|
||||
fmt::format("{}", dirent->start_sector);
|
||||
attributes["brother120.type"] = fmt::format("{}", dirent->brother_type);
|
||||
|
||||
return attributes;
|
||||
@@ -128,9 +129,9 @@ private:
|
||||
for (int block = 0; block < DIRECTORY_SECTORS; block++)
|
||||
{
|
||||
auto bytes = getLogicalSector(block);
|
||||
for (int d = 0; d < SECTOR_SIZE/16; d++, inode++)
|
||||
for (int d = 0; d < SECTOR_SIZE / 16; d++, inode++)
|
||||
{
|
||||
Bytes buffer = bytes.slice(d*16, 16);
|
||||
Bytes buffer = bytes.slice(d * 16, 16);
|
||||
if (buffer[0] == 0xf0)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -42,12 +42,18 @@ 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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> fluxSource,
|
||||
std::shared_ptr<FluxSink> fluxSink,
|
||||
std::shared_ptr<AbstractEncoder> encoder,
|
||||
std::shared_ptr<AbstractDecoder> decoder):
|
||||
_fluxSource(fluxSource),
|
||||
_fluxSink(fluxSink),
|
||||
_encoder(encoder),
|
||||
_decoder(decoder)
|
||||
{
|
||||
}
|
||||
@@ -20,47 +26,124 @@ public:
|
||||
std::shared_ptr<const Sector> 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);
|
||||
|
||||
return _sectorstore.get(track, side, sectorId);
|
||||
return _readSectors.get(track, side, sectorId);
|
||||
}
|
||||
|
||||
std::shared_ptr<Sector> put(
|
||||
unsigned track, unsigned side, unsigned sectorId)
|
||||
{
|
||||
trackid_t trackid(track, side);
|
||||
_changedtracks.insert(trackid);
|
||||
return _changedSectors.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);
|
||||
|
||||
_changedtracks.insert(trackid);
|
||||
return _sectorstore.put(track, side, sectorId);
|
||||
/* 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<unsigned>& 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 trackdata = readAndDecodeTrack(*_fluxSource, *_decoder, location);
|
||||
|
||||
for (const auto& sector : trackdata->sectors)
|
||||
*_sectorstore.put(track, side, sector->logicalSector) = *sector;
|
||||
*_readSectors.put(track, side, sector->logicalSector) = *sector;
|
||||
_loadedtracks.insert(trackid_t(track, side));
|
||||
}
|
||||
|
||||
std::shared_ptr<FluxSource> _fluxSource;
|
||||
std::shared_ptr<FluxSink> _fluxSink;
|
||||
std::shared_ptr<AbstractEncoder> _encoder;
|
||||
std::shared_ptr<AbstractDecoder> _decoder;
|
||||
|
||||
typedef std::pair<unsigned, unsigned> trackid_t;
|
||||
Image _sectorstore;
|
||||
Image _readSectors;
|
||||
Image _changedSectors;
|
||||
std::set<trackid_t> _loadedtracks;
|
||||
std::set<trackid_t> _changedtracks;
|
||||
};
|
||||
|
||||
std::unique_ptr<SectorInterface> SectorInterface::createFluxSectorInterface(
|
||||
std::shared_ptr<FluxSource> fluxSource, std::shared_ptr<AbstractDecoder> decoder)
|
||||
std::shared_ptr<FluxSource> fluxSource,
|
||||
std::shared_ptr<FluxSink> fluxSink,
|
||||
std::shared_ptr<AbstractEncoder> encoder,
|
||||
std::shared_ptr<AbstractDecoder> decoder)
|
||||
{
|
||||
return std::make_unique<FluxSectorInterface>(fluxSource, decoder);
|
||||
return std::make_unique<FluxSectorInterface>(
|
||||
fluxSource, fluxSink, encoder, decoder);
|
||||
}
|
||||
|
||||
@@ -5,17 +5,17 @@
|
||||
class ImageSectorInterface : public SectorInterface
|
||||
{
|
||||
public:
|
||||
ImageSectorInterface(std::shared_ptr<Image> image):
|
||||
_image(image)
|
||||
{}
|
||||
ImageSectorInterface(std::shared_ptr<Image> image): _image(image) {}
|
||||
|
||||
public:
|
||||
std::shared_ptr<const Sector> get(unsigned track, unsigned side, unsigned sectorId)
|
||||
std::shared_ptr<const Sector> get(
|
||||
unsigned track, unsigned side, unsigned sectorId)
|
||||
{
|
||||
return _image->get(track, side, sectorId);
|
||||
}
|
||||
|
||||
std::shared_ptr<Sector> put(unsigned track, unsigned side, unsigned sectorId)
|
||||
std::shared_ptr<Sector> put(
|
||||
unsigned track, unsigned side, unsigned sectorId)
|
||||
{
|
||||
return _image->put(track, side, sectorId);
|
||||
}
|
||||
@@ -24,8 +24,8 @@ private:
|
||||
std::shared_ptr<Image> _image;
|
||||
};
|
||||
|
||||
std::unique_ptr<SectorInterface> SectorInterface::createImageSectorInterface(std::shared_ptr<Image> image)
|
||||
std::unique_ptr<SectorInterface> SectorInterface::createImageSectorInterface(
|
||||
std::shared_ptr<Image> image)
|
||||
{
|
||||
return std::make_unique<ImageSectorInterface>(image);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -184,9 +227,17 @@ private:
|
||||
{
|
||||
public:
|
||||
HfsFile(hfsfile* file): _file(file) {}
|
||||
~HfsFile() { hfs_close(_file); }
|
||||
~HfsFile()
|
||||
{
|
||||
if (_file)
|
||||
hfs_close(_file);
|
||||
}
|
||||
|
||||
operator hfsfile*() const
|
||||
{
|
||||
return _file;
|
||||
}
|
||||
|
||||
operator hfsfile* () const { return _file; }
|
||||
private:
|
||||
hfsfile* _file;
|
||||
};
|
||||
@@ -195,9 +246,17 @@ private:
|
||||
{
|
||||
public:
|
||||
HfsDir(hfsdir* dir): _dir(dir) {}
|
||||
~HfsDir() { hfs_closedir(_dir); }
|
||||
~HfsDir()
|
||||
{
|
||||
if (_dir)
|
||||
hfs_closedir(_dir);
|
||||
}
|
||||
|
||||
operator hfsdir*() const
|
||||
{
|
||||
return _dir;
|
||||
}
|
||||
|
||||
operator hfsdir* () const { return _dir; }
|
||||
private:
|
||||
hfsdir* _dir;
|
||||
};
|
||||
@@ -227,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:
|
||||
|
||||
@@ -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> image);
|
||||
static std::unique_ptr<SectorInterface> createFluxSectorInterface(
|
||||
std::shared_ptr<FluxSource> fluxSource,
|
||||
std::shared_ptr<FluxSink> fluxSink,
|
||||
std::shared_ptr<AbstractEncoder> encoder,
|
||||
std::shared_ptr<AbstractDecoder> decoder);
|
||||
};
|
||||
|
||||
|
||||
@@ -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<std::unique_ptr<Dirent>> 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<std::string, std::string> Filesystem::getMetadata(const Path& path)
|
||||
{
|
||||
throw UnimplementedFilesystemException();
|
||||
}
|
||||
|
||||
void Filesystem::putMetadata(
|
||||
const Path& path, const std::map<std::string, std::string>& 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<SectorInterface> 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(
|
||||
|
||||
@@ -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<std::unique_ptr<Dirent>> 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<std::string, std::string> getMetadata(const Path& path)
|
||||
{
|
||||
throw UnimplementedFilesystemException();
|
||||
}
|
||||
|
||||
virtual void create();
|
||||
virtual FilesystemStatus check();
|
||||
virtual std::vector<std::unique_ptr<Dirent>> list(const Path& path);
|
||||
virtual Bytes getFile(const Path& path);
|
||||
virtual void putFile(const Path& path, const Bytes& data);
|
||||
virtual std::map<std::string, std::string> getMetadata(const Path& path);
|
||||
virtual void putMetadata(
|
||||
const Path& path, const std::map<std::string, std::string>& 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<std::string, std::string>& metadata);
|
||||
virtual void createDirectory(const Path& path);
|
||||
virtual void deleteFile(const Path& path);
|
||||
void flush();
|
||||
|
||||
protected:
|
||||
Filesystem(std::shared_ptr<SectorInterface> sectors);
|
||||
|
||||
@@ -2,53 +2,64 @@ syntax = "proto2";
|
||||
|
||||
import "lib/common.proto";
|
||||
|
||||
message AcornDfsProto {
|
||||
enum Flavour {
|
||||
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 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 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"];
|
||||
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"];
|
||||
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 AmigaFfsProto {
|
||||
message AmigaFfsProto {}
|
||||
|
||||
message MacHfsProto {}
|
||||
|
||||
message CbmfsProto
|
||||
{
|
||||
optional uint32 directory_track = 1 [
|
||||
default = 17,
|
||||
(help) = "which track the directory is on (zero-based numbering)"
|
||||
];
|
||||
}
|
||||
|
||||
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 {
|
||||
message FilesystemProto
|
||||
{
|
||||
oneof filesystem
|
||||
{
|
||||
AcornDfsProto acorndfs = 1;
|
||||
Brother120FsProto brother120 = 2;
|
||||
FatFsProto fatfs = 3;
|
||||
@@ -58,4 +69,3 @@ message FilesystemProto {
|
||||
CbmfsProto cbmfs = 7;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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[])
|
||||
{
|
||||
|
||||
51
src/fe-putfile.cc
Normal file
51
src/fe-putfile.cc
Normal file
@@ -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 <google/protobuf/text_format.h>
|
||||
#include <fstream>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<Filesystem> createFilesystemFromConfig()
|
||||
@@ -40,8 +44,10 @@ std::unique_ptr<Filesystem> createFilesystemFromConfig()
|
||||
if (config.has_flux_source())
|
||||
{
|
||||
std::shared_ptr<FluxSource> fluxSource(FluxSource::create(config.flux_source()));
|
||||
std::shared_ptr<FluxSink> fluxSink(FluxSink::create(config.flux_sink()));
|
||||
std::shared_ptr<AbstractEncoder> encoder(AbstractEncoder::create(config.encoder()));
|
||||
std::shared_ptr<AbstractDecoder> decoder(AbstractDecoder::create(config.decoder()));
|
||||
sectorInterface = SectorInterface::createFluxSectorInterface(fluxSource, decoder);
|
||||
sectorInterface = SectorInterface::createFluxSectorInterface(fluxSource, fluxSink, encoder, decoder);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -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<Command> 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.", },
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user