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];
|
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
|
Bytes Bytes::slice(unsigned start, unsigned len) const
|
||||||
{
|
{
|
||||||
start += _low;
|
start += _low;
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ public:
|
|||||||
|
|
||||||
Bytes* operator = (const Bytes& other);
|
Bytes* operator = (const Bytes& other);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static Bytes readFromFile(const std::string& filename);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/* General purpose methods */
|
/* 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();
|
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
|
std::shared_ptr<const Sector> Image::get(unsigned track, unsigned side, unsigned sectorid) const
|
||||||
{
|
{
|
||||||
static std::shared_ptr<const Sector> NONE;
|
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;
|
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()
|
void Image::calculateSize()
|
||||||
{
|
{
|
||||||
_geometry = {};
|
_geometry = {};
|
||||||
|
|||||||
@@ -39,8 +39,11 @@ public:
|
|||||||
public:
|
public:
|
||||||
void calculateSize();
|
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<const Sector> get(unsigned track, unsigned side, unsigned sectorId) const;
|
||||||
std::shared_ptr<Sector> put(unsigned track, unsigned side, unsigned sectorId);
|
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 begin() const { return const_iterator(_sectors.cbegin()); }
|
||||||
const_iterator end() const { return const_iterator(_sectors.cend()); }
|
const_iterator end() const { return const_iterator(_sectors.cend()); }
|
||||||
|
|||||||
@@ -187,8 +187,6 @@ void writeTracks(FluxSink& fluxSink,
|
|||||||
producer,
|
producer,
|
||||||
std::function<bool(const Location& location)> verifier)
|
std::function<bool(const Location& location)> verifier)
|
||||||
{
|
{
|
||||||
Logger() << fmt::format("Writing to: {}", (std::string)fluxSink);
|
|
||||||
|
|
||||||
for (const auto& location : Mapper::computeLocations())
|
for (const auto& location : Mapper::computeLocations())
|
||||||
{
|
{
|
||||||
testForEmergencyStop();
|
testForEmergencyStop();
|
||||||
@@ -287,44 +285,53 @@ void writeTracksAndVerify(FluxSink& fluxSink,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto wantedSectors = encoder.collectSectors(location, image);
|
Image wanted;
|
||||||
std::sort(wantedSectors.begin(),
|
for (const auto& sector : encoder.collectSectors(location, image))
|
||||||
wantedSectors.end(),
|
wanted.put(sector->logicalTrack, sector->logicalSide, sector->logicalSector)->data = sector->data;
|
||||||
sectorPointerSortPredicate);
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<const Sector>> gotSectors(
|
for (const auto& sector : trackFlux->sectors)
|
||||||
trackFlux->sectors.begin(), trackFlux->sectors.end());
|
{
|
||||||
std::sort(gotSectors.begin(),
|
const auto s = wanted.get(sector->logicalTrack, sector->logicalSide, sector->logicalSector);
|
||||||
gotSectors.end(),
|
if (!s)
|
||||||
sectorPointerSortPredicate);
|
{
|
||||||
|
Logger() << "spurious sector on verify";
|
||||||
if (!std::equal(gotSectors.begin(),
|
return false;
|
||||||
gotSectors.end(),
|
}
|
||||||
wantedSectors.begin(),
|
if (s->data != sector->data.slice(0, s->data.size()))
|
||||||
wantedSectors.end(),
|
{
|
||||||
sectorPointerEqualsPredicate))
|
Logger() << "data mismatch on verify";
|
||||||
{
|
return false;
|
||||||
Logger() << "good read but the data doesn't match";
|
}
|
||||||
return false;
|
wanted.erase(sector->logicalTrack, sector->logicalSide, sector->logicalSector);
|
||||||
}
|
}
|
||||||
|
if (!wanted.empty())
|
||||||
|
{
|
||||||
|
Logger() << "missing sector on verify";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeDiskCommand(std::shared_ptr<const Image> image,
|
void writeDiskCommand(const Image& image,
|
||||||
AbstractEncoder& encoder,
|
AbstractEncoder& encoder,
|
||||||
FluxSink& fluxSink,
|
FluxSink& fluxSink,
|
||||||
AbstractDecoder* decoder,
|
AbstractDecoder* decoder,
|
||||||
FluxSource* fluxSource)
|
FluxSource* fluxSource)
|
||||||
{
|
{
|
||||||
|
const Image* imagep = ℑ
|
||||||
|
std::unique_ptr<const Image> remapped;
|
||||||
if (config.has_sector_mapping())
|
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)
|
if (fluxSource && decoder)
|
||||||
writeTracksAndVerify(fluxSink, encoder, *fluxSource, *decoder, *image);
|
writeTracksAndVerify(fluxSink, encoder, *fluxSource, *decoder, *imagep);
|
||||||
else
|
else
|
||||||
writeTracks(fluxSink, encoder, *image);
|
writeTracks(fluxSink, encoder, *imagep);
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink)
|
void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ extern void fillBitmapTo(std::vector<bool>& bitmap,
|
|||||||
unsigned terminateAt,
|
unsigned terminateAt,
|
||||||
const std::vector<bool>& pattern);
|
const std::vector<bool>& pattern);
|
||||||
|
|
||||||
extern void writeDiskCommand(std::shared_ptr<const Image> image,
|
extern void writeDiskCommand(const Image& image,
|
||||||
AbstractEncoder& encoder,
|
AbstractEncoder& encoder,
|
||||||
FluxSink& fluxSink,
|
FluxSink& fluxSink,
|
||||||
AbstractDecoder* decoder = nullptr,
|
AbstractDecoder* decoder = nullptr,
|
||||||
|
|||||||
@@ -6,30 +6,32 @@
|
|||||||
class AcornDfsDirent : public Dirent
|
class AcornDfsDirent : public Dirent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AcornDfsDirent(int inode, const Bytes& bytes0, const Bytes& bytes1)
|
AcornDfsDirent(int inode, const Bytes& bytes0, const Bytes& bytes1)
|
||||||
{
|
{
|
||||||
filename += (char)(bytes0[7] & 0x7f);
|
filename += (char)(bytes0[7] & 0x7f);
|
||||||
filename += '.';
|
filename += '.';
|
||||||
for (int j = 0; j < 7; j++)
|
for (int j = 0; j < 7; j++)
|
||||||
filename += bytes0[j] & 0x7f;
|
filename += bytes0[j] & 0x7f;
|
||||||
filename = filename.substr(0, filename.find(' '));
|
filename = filename.substr(0, filename.find(' '));
|
||||||
|
|
||||||
this->inode = inode;
|
this->inode = inode;
|
||||||
start_sector = ((bytes1[6] & 0x03) << 8) | bytes1[7];
|
start_sector = ((bytes1[6] & 0x03) << 8) | bytes1[7];
|
||||||
load_address = ((bytes1[6] & 0x0c) << 14) | (bytes1[1] << 8) | bytes1[0];
|
load_address =
|
||||||
exec_address = ((bytes1[6] & 0xc0) << 10) | (bytes1[3] << 8) | bytes1[2];
|
((bytes1[6] & 0x0c) << 14) | (bytes1[1] << 8) | bytes1[0];
|
||||||
locked = bytes0[7] & 0x80;
|
exec_address =
|
||||||
length = ((bytes1[6] & 0x30) << 12) | (bytes1[5] << 8) | bytes1[4];
|
((bytes1[6] & 0xc0) << 10) | (bytes1[3] << 8) | bytes1[2];
|
||||||
file_type = TYPE_FILE;
|
locked = bytes0[7] & 0x80;
|
||||||
mode = locked ? "L" : "";
|
length = ((bytes1[6] & 0x30) << 12) | (bytes1[5] << 8) | bytes1[4];
|
||||||
}
|
file_type = TYPE_FILE;
|
||||||
|
mode = locked ? "L" : "";
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int inode;
|
int inode;
|
||||||
uint32_t start_sector;
|
uint32_t start_sector;
|
||||||
uint32_t load_address;
|
uint32_t load_address;
|
||||||
uint32_t exec_address;
|
uint32_t exec_address;
|
||||||
bool locked;
|
bool locked;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AcornDfsFilesystem : public Filesystem
|
class AcornDfsFilesystem : public Filesystem
|
||||||
@@ -37,7 +39,7 @@ class AcornDfsFilesystem : public Filesystem
|
|||||||
public:
|
public:
|
||||||
AcornDfsFilesystem(
|
AcornDfsFilesystem(
|
||||||
const AcornDfsProto& config, std::shared_ptr<SectorInterface> sectors):
|
const AcornDfsProto& config, std::shared_ptr<SectorInterface> sectors):
|
||||||
Filesystem(sectors),
|
Filesystem(sectors),
|
||||||
_config(config)
|
_config(config)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -53,46 +55,49 @@ public:
|
|||||||
throw FileNotFoundException();
|
throw FileNotFoundException();
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Dirent>> result;
|
std::vector<std::unique_ptr<Dirent>> result;
|
||||||
for (auto& dirent : findAllFiles())
|
for (auto& dirent : findAllFiles())
|
||||||
result.push_back(std::move(dirent));
|
result.push_back(std::move(dirent));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bytes getFile(const Path& path)
|
Bytes getFile(const Path& path)
|
||||||
{
|
{
|
||||||
auto dirent = findFile(path);
|
auto dirent = findFile(path);
|
||||||
int sectors = (dirent->length + 255) / 256;
|
int sectors = (dirent->length + 255) / 256;
|
||||||
|
|
||||||
Bytes data;
|
Bytes data;
|
||||||
ByteWriter bw(data);
|
ByteWriter bw(data);
|
||||||
for (int i = 0; i < sectors; i++)
|
for (int i = 0; i < sectors; i++)
|
||||||
{
|
{
|
||||||
auto sector = getLogicalSector(dirent->start_sector + i);
|
auto sector = getLogicalSector(dirent->start_sector + i);
|
||||||
bw.append(sector);
|
bw.append(sector);
|
||||||
}
|
}
|
||||||
|
|
||||||
data.resize(dirent->length);
|
data.resize(dirent->length);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> getMetadata(const Path& path)
|
std::map<std::string, std::string> getMetadata(const Path& path)
|
||||||
{
|
{
|
||||||
std::map<std::string, std::string> attributes;
|
std::map<std::string, std::string> attributes;
|
||||||
|
|
||||||
auto dirent = findFile(path);
|
auto dirent = findFile(path);
|
||||||
attributes["filename"] = dirent->filename;
|
attributes["filename"] = dirent->filename;
|
||||||
attributes["length"] = fmt::format("{}", dirent->length);
|
attributes["length"] = fmt::format("{}", dirent->length);
|
||||||
attributes["type"] = "file";
|
attributes["type"] = "file";
|
||||||
attributes["mode"] = dirent->mode;
|
attributes["mode"] = dirent->mode;
|
||||||
attributes["acorndfs.inode"] = fmt::format("{}", dirent->inode);
|
attributes["acorndfs.inode"] = fmt::format("{}", dirent->inode);
|
||||||
attributes["acorndfs.start_sector"] = fmt::format("{}", dirent->start_sector);
|
attributes["acorndfs.start_sector"] =
|
||||||
attributes["acorndfs.load_address"] = fmt::format("0x{:x}", dirent->load_address);
|
fmt::format("{}", dirent->start_sector);
|
||||||
attributes["acorndfs.exec_address"] = fmt::format("0x{:x}", dirent->exec_address);
|
attributes["acorndfs.load_address"] =
|
||||||
attributes["acorndfs.locked"] = fmt::format("{}", dirent->locked);
|
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:
|
private:
|
||||||
std::vector<std::unique_ptr<AcornDfsDirent>> findAllFiles()
|
std::vector<std::unique_ptr<AcornDfsDirent>> findAllFiles()
|
||||||
@@ -110,25 +115,26 @@ private:
|
|||||||
auto bytes0 = sector0.slice(i * 8 + 8, 8);
|
auto bytes0 = sector0.slice(i * 8 + 8, 8);
|
||||||
auto bytes1 = sector1.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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<AcornDfsDirent> findFile(const Path& path)
|
std::unique_ptr<AcornDfsDirent> findFile(const Path& path)
|
||||||
{
|
{
|
||||||
if (path.size() != 1)
|
if (path.size() != 1)
|
||||||
throw BadPathException();
|
throw BadPathException();
|
||||||
|
|
||||||
for (auto& dirent : findAllFiles())
|
for (auto& dirent : findAllFiles())
|
||||||
{
|
{
|
||||||
if (dirent->filename == path[0])
|
if (dirent->filename == path[0])
|
||||||
return std::move(dirent);
|
return std::move(dirent);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw FileNotFoundException();
|
throw FileNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const AcornDfsProto& _config;
|
const AcornDfsProto& _config;
|
||||||
|
|||||||
@@ -147,6 +147,30 @@ public:
|
|||||||
return bytes;
|
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:
|
private:
|
||||||
class AdfEntry
|
class AdfEntry
|
||||||
{
|
{
|
||||||
@@ -270,6 +294,8 @@ public:
|
|||||||
RETCODE adfNativeWriteSector(
|
RETCODE adfNativeWriteSector(
|
||||||
struct Device* dev, int32_t sector, int size, uint8_t* buffer)
|
struct Device* dev, int32_t sector, int size, uint8_t* buffer)
|
||||||
{
|
{
|
||||||
|
Bytes bytes(buffer, size);
|
||||||
|
putLogicalSector(sector, bytes);
|
||||||
return RC_OK;
|
return RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ void AppleSingle::parse(const Bytes& bytes)
|
|||||||
|
|
||||||
case 9:
|
case 9:
|
||||||
{
|
{
|
||||||
Bytes finderinfo = bytes.slice(offset, length);
|
Bytes finderinfo = bytes.slice(offset, length);
|
||||||
type = finderinfo.slice(0, 4);
|
type = finderinfo.slice(0, 4);
|
||||||
creator = finderinfo.slice(4, 4);
|
creator = finderinfo.slice(4, 4);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,32 +27,32 @@ static constexpr int DIRECTORY_SECTORS = 8;
|
|||||||
class Brother120Dirent : public Dirent
|
class Brother120Dirent : public Dirent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Brother120Dirent(int inode, const Bytes& bytes)
|
Brother120Dirent(int inode, const Bytes& bytes)
|
||||||
{
|
{
|
||||||
ByteReader br(bytes);
|
ByteReader br(bytes);
|
||||||
filename = br.read(8);
|
filename = br.read(8);
|
||||||
filename = filename.substr(0, filename.find(' '));
|
filename = filename.substr(0, filename.find(' '));
|
||||||
|
|
||||||
this->inode = inode;
|
this->inode = inode;
|
||||||
brother_type = br.read_8();
|
brother_type = br.read_8();
|
||||||
start_sector = br.read_be16();
|
start_sector = br.read_be16();
|
||||||
length = br.read_8() * SECTOR_SIZE;
|
length = br.read_8() * SECTOR_SIZE;
|
||||||
file_type = TYPE_FILE;
|
file_type = TYPE_FILE;
|
||||||
mode = "";
|
mode = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int inode;
|
int inode;
|
||||||
int brother_type;
|
int brother_type;
|
||||||
uint32_t start_sector;
|
uint32_t start_sector;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Brother120Filesystem : public Filesystem
|
class Brother120Filesystem : public Filesystem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Brother120Filesystem(
|
Brother120Filesystem(const Brother120FsProto& config,
|
||||||
const Brother120FsProto& config, std::shared_ptr<SectorInterface> sectors):
|
std::shared_ptr<SectorInterface> sectors):
|
||||||
Filesystem(sectors),
|
Filesystem(sectors),
|
||||||
_config(config)
|
_config(config)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -64,97 +64,98 @@ public:
|
|||||||
|
|
||||||
std::vector<std::unique_ptr<Dirent>> list(const Path& path)
|
std::vector<std::unique_ptr<Dirent>> list(const Path& path)
|
||||||
{
|
{
|
||||||
if (!path.empty())
|
if (!path.empty())
|
||||||
throw FileNotFoundException();
|
throw FileNotFoundException();
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Dirent>> result;
|
std::vector<std::unique_ptr<Dirent>> result;
|
||||||
for (auto& dirent : findAllFiles())
|
for (auto& dirent : findAllFiles())
|
||||||
result.push_back(std::move(dirent));
|
result.push_back(std::move(dirent));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bytes getFile(const Path& path)
|
Bytes getFile(const Path& path)
|
||||||
{
|
{
|
||||||
auto fat = readFat();
|
auto fat = readFat();
|
||||||
auto dirent = findFile(path);
|
auto dirent = findFile(path);
|
||||||
int sector = dirent->start_sector;
|
int sector = dirent->start_sector;
|
||||||
|
|
||||||
Bytes data;
|
Bytes data;
|
||||||
ByteWriter bw(data);
|
ByteWriter bw(data);
|
||||||
while ((sector != 0) && (sector != 0xffff))
|
while ((sector != 0) && (sector != 0xffff))
|
||||||
{
|
{
|
||||||
bw += getLogicalSector(sector - 1);
|
bw += getLogicalSector(sector - 1);
|
||||||
sector = fat.at(sector);
|
sector = fat.at(sector);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> getMetadata(const Path& path)
|
std::map<std::string, std::string> getMetadata(const Path& path)
|
||||||
{
|
{
|
||||||
std::map<std::string, std::string> attributes;
|
std::map<std::string, std::string> attributes;
|
||||||
|
|
||||||
auto dirent = findFile(path);
|
auto dirent = findFile(path);
|
||||||
attributes["filename"] = dirent->filename;
|
attributes["filename"] = dirent->filename;
|
||||||
attributes["length"] = fmt::format("{}", dirent->length);
|
attributes["length"] = fmt::format("{}", dirent->length);
|
||||||
attributes["type"] = "file";
|
attributes["type"] = "file";
|
||||||
attributes["mode"] = dirent->mode;
|
attributes["mode"] = dirent->mode;
|
||||||
attributes["brother120.inode"] = fmt::format("{}", dirent->inode);
|
attributes["brother120.inode"] = fmt::format("{}", dirent->inode);
|
||||||
attributes["brother120.start_sector"] = fmt::format("{}", dirent->start_sector);
|
attributes["brother120.start_sector"] =
|
||||||
attributes["brother120.type"] = fmt::format("{}", dirent->brother_type);
|
fmt::format("{}", dirent->start_sector);
|
||||||
|
attributes["brother120.type"] = fmt::format("{}", dirent->brother_type);
|
||||||
|
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<uint32_t> readFat()
|
std::vector<uint32_t> readFat()
|
||||||
{
|
{
|
||||||
Bytes bytes = getLogicalSector(FAT_START_SECTOR, FAT_SECTORS);
|
Bytes bytes = getLogicalSector(FAT_START_SECTOR, FAT_SECTORS);
|
||||||
ByteReader br(bytes);
|
ByteReader br(bytes);
|
||||||
std::vector<uint32_t> table;
|
std::vector<uint32_t> table;
|
||||||
|
|
||||||
table.push_back(0xffff);
|
table.push_back(0xffff);
|
||||||
for (int sector = 1; sector != SECTOR_COUNT; sector++)
|
for (int sector = 1; sector != SECTOR_COUNT; sector++)
|
||||||
table.push_back(br.read_be16());
|
table.push_back(br.read_be16());
|
||||||
|
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Brother120Dirent>> findAllFiles()
|
std::vector<std::unique_ptr<Brother120Dirent>> findAllFiles()
|
||||||
{
|
{
|
||||||
std::vector<std::unique_ptr<Brother120Dirent>> result;
|
std::vector<std::unique_ptr<Brother120Dirent>> result;
|
||||||
int inode = 0;
|
int inode = 0;
|
||||||
for (int block = 0; block < DIRECTORY_SECTORS; block++)
|
for (int block = 0; block < DIRECTORY_SECTORS; block++)
|
||||||
{
|
{
|
||||||
auto bytes = getLogicalSector(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)
|
if (buffer[0] == 0xf0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
result.push_back(
|
result.push_back(
|
||||||
std::make_unique<Brother120Dirent>(inode, buffer));
|
std::make_unique<Brother120Dirent>(inode, buffer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Brother120Dirent> findFile(const Path& path)
|
std::unique_ptr<Brother120Dirent> findFile(const Path& path)
|
||||||
{
|
{
|
||||||
if (path.size() != 1)
|
if (path.size() != 1)
|
||||||
throw BadPathException();
|
throw BadPathException();
|
||||||
|
|
||||||
for (auto& dirent : findAllFiles())
|
for (auto& dirent : findAllFiles())
|
||||||
{
|
{
|
||||||
if (dirent->filename == path[0])
|
if (dirent->filename == path[0])
|
||||||
return std::move(dirent);
|
return std::move(dirent);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw FileNotFoundException();
|
throw FileNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Brother120FsProto& _config;
|
const Brother120FsProto& _config;
|
||||||
|
|||||||
146
lib/vfs/cbmfs.cc
146
lib/vfs/cbmfs.cc
@@ -6,11 +6,11 @@
|
|||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
DEL,
|
DEL,
|
||||||
SEQ,
|
SEQ,
|
||||||
PRG,
|
PRG,
|
||||||
USR,
|
USR,
|
||||||
REL
|
REL
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::string fromPetscii(const Bytes& bytes)
|
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)
|
static std::string toMode(uint8_t cbm_type)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
if (cbm_type & 0x40)
|
if (cbm_type & 0x40)
|
||||||
ss << 'L';
|
ss << 'L';
|
||||||
if (cbm_type & 0x80)
|
if (cbm_type & 0x80)
|
||||||
ss << 'S';
|
ss << 'S';
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string toFileType(uint8_t cbm_type)
|
static std::string toFileType(uint8_t cbm_type)
|
||||||
{
|
{
|
||||||
switch (cbm_type & 0x0f)
|
switch (cbm_type & 0x0f)
|
||||||
{
|
{
|
||||||
case DEL: return "DEL";
|
case DEL:
|
||||||
case SEQ: return "SEQ";
|
return "DEL";
|
||||||
case PRG: return "PRG";
|
case SEQ:
|
||||||
case USR: return "USR";
|
return "SEQ";
|
||||||
case REL: return "REL";
|
case PRG:
|
||||||
default: return fmt::format("[bad type {:x}]", cbm_type & 0x0f);
|
return "PRG";
|
||||||
}
|
case USR:
|
||||||
|
return "USR";
|
||||||
|
case REL:
|
||||||
|
return "REL";
|
||||||
|
default:
|
||||||
|
return fmt::format("[bad type {:x}]", cbm_type & 0x0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CbmfsDirent : public Dirent
|
class CbmfsDirent : public Dirent
|
||||||
@@ -63,7 +69,7 @@ public:
|
|||||||
start_track = br.read_8();
|
start_track = br.read_8();
|
||||||
start_sector = 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);
|
filename = fromPetscii(filenameBytes);
|
||||||
side_track = br.read_8();
|
side_track = br.read_8();
|
||||||
side_sector = br.read_8();
|
side_sector = br.read_8();
|
||||||
@@ -103,8 +109,8 @@ public:
|
|||||||
|
|
||||||
std::vector<std::unique_ptr<Dirent>> list(const Path& path)
|
std::vector<std::unique_ptr<Dirent>> list(const Path& path)
|
||||||
{
|
{
|
||||||
if (path.size() != 0)
|
if (path.size() != 0)
|
||||||
throw BadPathException();
|
throw BadPathException();
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Dirent>> results;
|
std::vector<std::unique_ptr<Dirent>> results;
|
||||||
uint8_t t = _config.directory_track();
|
uint8_t t = _config.directory_track();
|
||||||
@@ -131,64 +137,64 @@ public:
|
|||||||
|
|
||||||
std::map<std::string, std::string> getMetadata(const Path& path)
|
std::map<std::string, std::string> getMetadata(const Path& path)
|
||||||
{
|
{
|
||||||
if (path.size() != 1)
|
|
||||||
throw BadPathException();
|
|
||||||
auto de = findFile(unhex(path[0]));
|
|
||||||
if (!de)
|
|
||||||
throw FileNotFoundException();
|
|
||||||
|
|
||||||
std::map<std::string, std::string> 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)
|
if (path.size() != 1)
|
||||||
throw BadPathException();
|
throw BadPathException();
|
||||||
auto de = findFile(unhex(path[0]));
|
auto de = findFile(unhex(path[0]));
|
||||||
if (!de)
|
if (!de)
|
||||||
throw FileNotFoundException();
|
throw FileNotFoundException();
|
||||||
if (de->cbm_type == REL)
|
|
||||||
throw UnimplementedFilesystemException("cannot read .REL files");
|
|
||||||
|
|
||||||
Bytes bytes;
|
std::map<std::string, std::string> attributes;
|
||||||
ByteWriter bw(bytes);
|
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 t = de->start_track - 1;
|
||||||
uint8_t s = de->start_sector;
|
uint8_t s = de->start_sector;
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
auto b = getSector(t, 0, s);
|
auto b = getSector(t, 0, s);
|
||||||
|
|
||||||
if (b[0])
|
if (b[0])
|
||||||
bw += b.slice(2);
|
bw += b.slice(2);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bw += b.slice(2, b[1]);
|
bw += b.slice(2, b[1]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
t = b[0] - 1;
|
t = b[0] - 1;
|
||||||
s = b[1];
|
s = b[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<CbmfsDirent> findFile(const std::string& filename)
|
std::unique_ptr<CbmfsDirent> findFile(const std::string& filename)
|
||||||
{
|
{
|
||||||
uint8_t t = _config.directory_track();
|
uint8_t t = _config.directory_track();
|
||||||
uint8_t s = 1;
|
uint8_t s = 1;
|
||||||
while (t != 0xff)
|
while (t != 0xff)
|
||||||
@@ -201,17 +207,17 @@ private:
|
|||||||
if (dbuf[2] == 0)
|
if (dbuf[2] == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto de = std::make_unique<CbmfsDirent>(dbuf);
|
auto de = std::make_unique<CbmfsDirent>(dbuf);
|
||||||
if (de->filename == filename)
|
if (de->filename == filename)
|
||||||
return de;
|
return de;
|
||||||
}
|
}
|
||||||
|
|
||||||
t = b[0] - 1;
|
t = b[0] - 1;
|
||||||
s = b[1];
|
s = b[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const CbmfsProto& _config;
|
const CbmfsProto& _config;
|
||||||
|
|||||||
@@ -116,6 +116,30 @@ public:
|
|||||||
return bytes;
|
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:
|
public:
|
||||||
DRESULT diskRead(BYTE* buffer, LBA_t sector, UINT count)
|
DRESULT diskRead(BYTE* buffer, LBA_t sector, UINT count)
|
||||||
{
|
{
|
||||||
@@ -126,7 +150,9 @@ public:
|
|||||||
|
|
||||||
DRESULT diskWrite(const BYTE* buffer, LBA_t sector, UINT count)
|
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)
|
DRESULT diskIoctl(BYTE cmd, void* buffer)
|
||||||
@@ -163,6 +189,16 @@ private:
|
|||||||
case FR_NO_PATH:
|
case FR_NO_PATH:
|
||||||
throw FileNotFoundException();
|
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:
|
case FR_NO_FILESYSTEM:
|
||||||
throw BadFilesystemException();
|
throw BadFilesystemException();
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,20 @@
|
|||||||
#include "lib/readerwriter.h"
|
#include "lib/readerwriter.h"
|
||||||
#include "lib/decoders/decoders.h"
|
#include "lib/decoders/decoders.h"
|
||||||
#include "lib/fluxsource/fluxsource.h"
|
#include "lib/fluxsource/fluxsource.h"
|
||||||
|
#include "lib/layout.h"
|
||||||
|
#include "lib/proto.h"
|
||||||
#include "lib/mapper.h"
|
#include "lib/mapper.h"
|
||||||
|
|
||||||
class FluxSectorInterface : public SectorInterface
|
class FluxSectorInterface : public SectorInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FluxSectorInterface(std::shared_ptr<FluxSource> fluxSource,
|
FluxSectorInterface(std::shared_ptr<FluxSource> fluxSource,
|
||||||
|
std::shared_ptr<FluxSink> fluxSink,
|
||||||
|
std::shared_ptr<AbstractEncoder> encoder,
|
||||||
std::shared_ptr<AbstractDecoder> decoder):
|
std::shared_ptr<AbstractDecoder> decoder):
|
||||||
_fluxSource(fluxSource),
|
_fluxSource(fluxSource),
|
||||||
|
_fluxSink(fluxSink),
|
||||||
|
_encoder(encoder),
|
||||||
_decoder(decoder)
|
_decoder(decoder)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -20,47 +26,124 @@ public:
|
|||||||
std::shared_ptr<const Sector> get(
|
std::shared_ptr<const Sector> get(
|
||||||
unsigned track, unsigned side, unsigned sectorId)
|
unsigned track, unsigned side, unsigned sectorId)
|
||||||
{
|
{
|
||||||
|
auto it = _changedSectors.get(track, side, sectorId);
|
||||||
|
if (it)
|
||||||
|
return it;
|
||||||
|
|
||||||
trackid_t trackid(track, side);
|
trackid_t trackid(track, side);
|
||||||
if (_loadedtracks.find(trackid) == _loadedtracks.end())
|
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<Sector> put(
|
std::shared_ptr<Sector> put(
|
||||||
unsigned track, unsigned side, unsigned sectorId)
|
unsigned track, unsigned side, unsigned sectorId)
|
||||||
{
|
{
|
||||||
trackid_t trackid(track, side);
|
trackid_t trackid(track, side);
|
||||||
if (_loadedtracks.find(trackid) == _loadedtracks.end())
|
_changedtracks.insert(trackid);
|
||||||
populateSectors(track, side);
|
return _changedSectors.put(track, side, sectorId);
|
||||||
|
}
|
||||||
|
|
||||||
_changedtracks.insert(trackid);
|
void flush()
|
||||||
return _sectorstore.put(track, side, sectorId);
|
{
|
||||||
|
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:
|
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)
|
void populateSectors(unsigned track, unsigned side)
|
||||||
{
|
{
|
||||||
auto location = Mapper::computeLocationFor(track, side);
|
auto location = Mapper::computeLocationFor(track, side);
|
||||||
auto trackdata =
|
auto trackdata = readAndDecodeTrack(*_fluxSource, *_decoder, location);
|
||||||
readAndDecodeTrack(*_fluxSource, *_decoder, location);
|
|
||||||
|
|
||||||
for (const auto& sector : trackdata->sectors)
|
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));
|
_loadedtracks.insert(trackid_t(track, side));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<FluxSource> _fluxSource;
|
std::shared_ptr<FluxSource> _fluxSource;
|
||||||
|
std::shared_ptr<FluxSink> _fluxSink;
|
||||||
|
std::shared_ptr<AbstractEncoder> _encoder;
|
||||||
std::shared_ptr<AbstractDecoder> _decoder;
|
std::shared_ptr<AbstractDecoder> _decoder;
|
||||||
|
|
||||||
typedef std::pair<unsigned, unsigned> trackid_t;
|
typedef std::pair<unsigned, unsigned> trackid_t;
|
||||||
Image _sectorstore;
|
Image _readSectors;
|
||||||
|
Image _changedSectors;
|
||||||
std::set<trackid_t> _loadedtracks;
|
std::set<trackid_t> _loadedtracks;
|
||||||
std::set<trackid_t> _changedtracks;
|
std::set<trackid_t> _changedtracks;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<SectorInterface> SectorInterface::createFluxSectorInterface(
|
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,27 +5,27 @@
|
|||||||
class ImageSectorInterface : public SectorInterface
|
class ImageSectorInterface : public SectorInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ImageSectorInterface(std::shared_ptr<Image> image):
|
ImageSectorInterface(std::shared_ptr<Image> image): _image(image) {}
|
||||||
_image(image)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public:
|
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);
|
{
|
||||||
}
|
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);
|
{
|
||||||
}
|
return _image->put(track, side, sectorId);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Image> _image;
|
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);
|
return std::make_unique<ImageSectorInterface>(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,8 +130,8 @@ public:
|
|||||||
|
|
||||||
hfsdirent de;
|
hfsdirent de;
|
||||||
hfs_fstat(file, &de);
|
hfs_fstat(file, &de);
|
||||||
a.creator = Bytes(de.u.file.creator);
|
a.creator = Bytes(de.u.file.creator);
|
||||||
a.type = Bytes(de.u.file.type);
|
a.type = Bytes(de.u.file.type);
|
||||||
|
|
||||||
hfs_setfork(file, 0);
|
hfs_setfork(file, 0);
|
||||||
a.data = readBytes(file);
|
a.data = readBytes(file);
|
||||||
@@ -141,6 +141,38 @@ public:
|
|||||||
return a.render();
|
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:
|
private:
|
||||||
Bytes readBytes(hfsfile* file)
|
Bytes readBytes(hfsfile* file)
|
||||||
{
|
{
|
||||||
@@ -161,6 +193,17 @@ private:
|
|||||||
return bytes;
|
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:
|
private:
|
||||||
class HfsMount
|
class HfsMount
|
||||||
{
|
{
|
||||||
@@ -183,23 +226,39 @@ private:
|
|||||||
class HfsFile
|
class HfsFile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HfsFile(hfsfile* file): _file(file) {}
|
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:
|
private:
|
||||||
hfsfile* _file;
|
hfsfile* _file;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HfsDir
|
class HfsDir
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HfsDir(hfsdir* dir): _dir(dir) {}
|
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:
|
private:
|
||||||
hfsdir* _dir;
|
hfsdir* _dir;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -227,7 +286,10 @@ private:
|
|||||||
void** priv, const void* buffer, unsigned long len);
|
void** priv, const void* buffer, unsigned long len);
|
||||||
unsigned long hfsWrite(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:
|
private:
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
class Image;
|
class Image;
|
||||||
class Sector;
|
class Sector;
|
||||||
class FluxSource;
|
class FluxSource;
|
||||||
|
class FluxSink;
|
||||||
class AbstractDecoder;
|
class AbstractDecoder;
|
||||||
|
class AbstractEncoder;
|
||||||
|
|
||||||
class SectorInterface
|
class SectorInterface
|
||||||
{
|
{
|
||||||
@@ -21,6 +23,8 @@ public:
|
|||||||
std::shared_ptr<Image> image);
|
std::shared_ptr<Image> image);
|
||||||
static std::unique_ptr<SectorInterface> createFluxSectorInterface(
|
static std::unique_ptr<SectorInterface> createFluxSectorInterface(
|
||||||
std::shared_ptr<FluxSource> fluxSource,
|
std::shared_ptr<FluxSource> fluxSource,
|
||||||
|
std::shared_ptr<FluxSink> fluxSink,
|
||||||
|
std::shared_ptr<AbstractEncoder> encoder,
|
||||||
std::shared_ptr<AbstractDecoder> decoder);
|
std::shared_ptr<AbstractDecoder> decoder);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,57 @@ std::string Path::to_str(const std::string sep) const
|
|||||||
return join(*this, sep);
|
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):
|
Filesystem::Filesystem(std::shared_ptr<SectorInterface> sectors):
|
||||||
_sectors(sectors)
|
_sectors(sectors)
|
||||||
{
|
{
|
||||||
@@ -125,8 +176,19 @@ void Filesystem::putLogicalSector(uint32_t number, const Bytes& data)
|
|||||||
if (number >= _locations.size())
|
if (number >= _locations.size())
|
||||||
throw BadFilesystemException();
|
throw BadFilesystemException();
|
||||||
|
|
||||||
auto& i = _locations[number];
|
unsigned pos = 0;
|
||||||
_sectors->put(std::get<0>(i), std::get<1>(i), std::get<2>(i))->data = data;
|
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(
|
unsigned Filesystem::getOffsetOfSector(
|
||||||
|
|||||||
@@ -60,6 +60,12 @@ public:
|
|||||||
BadFilesystemException(): FilesystemException("Invalid filesystem") {}
|
BadFilesystemException(): FilesystemException("Invalid filesystem") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CannotWriteException : public FilesystemException
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CannotWriteException(): FilesystemException("Cannot write file") {}
|
||||||
|
};
|
||||||
|
|
||||||
class ReadOnlyFilesystemException : public FilesystemException
|
class ReadOnlyFilesystemException : public FilesystemException
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -95,51 +101,17 @@ public:
|
|||||||
class Filesystem
|
class Filesystem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void create()
|
virtual void create();
|
||||||
{
|
virtual FilesystemStatus check();
|
||||||
throw UnimplementedFilesystemException();
|
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 FilesystemStatus check()
|
virtual std::map<std::string, std::string> getMetadata(const Path& path);
|
||||||
{
|
|
||||||
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 putMetadata(
|
virtual void putMetadata(
|
||||||
const Path& path, const std::map<std::string, std::string>& metadata)
|
const Path& path, const std::map<std::string, std::string>& metadata);
|
||||||
{
|
virtual void createDirectory(const Path& path);
|
||||||
throw UnimplementedFilesystemException();
|
virtual void deleteFile(const Path& path);
|
||||||
}
|
void flush();
|
||||||
|
|
||||||
virtual void createDirectory(const Path& path)
|
|
||||||
{
|
|
||||||
throw UnimplementedFilesystemException();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void deleteFile(const Path& path)
|
|
||||||
{
|
|
||||||
throw UnimplementedFilesystemException();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Filesystem(std::shared_ptr<SectorInterface> sectors);
|
Filesystem(std::shared_ptr<SectorInterface> sectors);
|
||||||
@@ -149,7 +121,7 @@ protected:
|
|||||||
Bytes getLogicalSector(uint32_t number, uint32_t count = 1);
|
Bytes getLogicalSector(uint32_t number, uint32_t count = 1);
|
||||||
void putLogicalSector(uint32_t number, const Bytes& data);
|
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 getLogicalSectorCount();
|
||||||
unsigned getLogicalSectorSize(unsigned track = 0, unsigned side = 0);
|
unsigned getLogicalSectorSize(unsigned track = 0, unsigned side = 0);
|
||||||
|
|
||||||
|
|||||||
@@ -2,60 +2,70 @@ syntax = "proto2";
|
|||||||
|
|
||||||
import "lib/common.proto";
|
import "lib/common.proto";
|
||||||
|
|
||||||
message AcornDfsProto {
|
message AcornDfsProto
|
||||||
enum Flavour {
|
{
|
||||||
UNDEFINED = 0;
|
enum Flavour
|
||||||
ACORN_DFS = 1;
|
{
|
||||||
}
|
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 FilesystemProto
|
||||||
message Location {
|
{
|
||||||
optional uint32 track = 1 [(help) = "track number"];
|
oneof filesystem
|
||||||
optional uint32 side = 2 [(help) = "side number"];
|
{
|
||||||
optional uint32 sector = 3 [(help) = "sector ID"];
|
AcornDfsProto acorndfs = 1;
|
||||||
}
|
Brother120FsProto brother120 = 2;
|
||||||
|
FatFsProto fatfs = 3;
|
||||||
message Padding {
|
CpmFsProto cpmfs = 4;
|
||||||
optional uint32 amount = 1 [(help) = "number of sectors of padding to insert"];
|
AmigaFfsProto amigaffs = 5;
|
||||||
optional uint32 every = 2 [(help) = "insert padding after this many sectors"];
|
MacHfsProto machfs = 6;
|
||||||
}
|
CbmfsProto cbmfs = 7;
|
||||||
|
}
|
||||||
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 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ FLUXENGINE_SRCS = \
|
|||||||
src/fe-getfileinfo.cc \
|
src/fe-getfileinfo.cc \
|
||||||
src/fe-inspect.cc \
|
src/fe-inspect.cc \
|
||||||
src/fe-ls.cc \
|
src/fe-ls.cc \
|
||||||
|
src/fe-putfile.cc \
|
||||||
src/fe-rawread.cc \
|
src/fe-rawread.cc \
|
||||||
src/fe-rawwrite.cc \
|
src/fe-rawwrite.cc \
|
||||||
src/fe-read.cc \
|
src/fe-read.cc \
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
|
|
||||||
static FlagGroup flags({&fileFlags});
|
static FlagGroup flags({&fileFlags});
|
||||||
|
|
||||||
static StringFlag directory({"-p", "--path"}, "path to work on", "");
|
static StringFlag directory({"-p", "--path"}, "disk path to work on", "");
|
||||||
static StringFlag output({"-o", "--output"}, "local filename to write to", "");
|
static StringFlag output({"-l", "--local"}, "local filename to write to", "");
|
||||||
|
|
||||||
int mainGetFile(int argc, const char* argv[])
|
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())
|
if (verify && config.has_flux_source() && config.flux_source().has_drive())
|
||||||
fluxSource = FluxSource::create(config.flux_source());
|
fluxSource = FluxSource::create(config.flux_source());
|
||||||
|
|
||||||
writeDiskCommand(image, *encoder, *fluxSink, decoder.get(), fluxSource.get());
|
writeDiskCommand(*image, *encoder, *fluxSink, decoder.get(), fluxSource.get());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
#include "readerwriter.h"
|
#include "readerwriter.h"
|
||||||
#include "lib/decoders/decoders.h"
|
#include "lib/decoders/decoders.h"
|
||||||
|
#include "lib/encoders/encoders.h"
|
||||||
#include "lib/fluxsource/fluxsource.h"
|
#include "lib/fluxsource/fluxsource.h"
|
||||||
|
#include "lib/fluxsink/fluxsink.h"
|
||||||
#include "lib/imagereader/imagereader.h"
|
#include "lib/imagereader/imagereader.h"
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
#include "fluxengine.h"
|
#include "fluxengine.h"
|
||||||
@@ -32,6 +34,8 @@ static StringFlag flux({"-f", "--flux"},
|
|||||||
{
|
{
|
||||||
FluxSource::updateConfigForFilename(
|
FluxSource::updateConfigForFilename(
|
||||||
config.mutable_flux_source(), value);
|
config.mutable_flux_source(), value);
|
||||||
|
FluxSink::updateConfigForFilename(
|
||||||
|
config.mutable_flux_sink(), value);
|
||||||
});
|
});
|
||||||
|
|
||||||
std::unique_ptr<Filesystem> createFilesystemFromConfig()
|
std::unique_ptr<Filesystem> createFilesystemFromConfig()
|
||||||
@@ -40,8 +44,10 @@ std::unique_ptr<Filesystem> createFilesystemFromConfig()
|
|||||||
if (config.has_flux_source())
|
if (config.has_flux_source())
|
||||||
{
|
{
|
||||||
std::shared_ptr<FluxSource> fluxSource(FluxSource::create(config.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()));
|
std::shared_ptr<AbstractDecoder> decoder(AbstractDecoder::create(config.decoder()));
|
||||||
sectorInterface = SectorInterface::createFluxSectorInterface(fluxSource, decoder);
|
sectorInterface = SectorInterface::createFluxSectorInterface(fluxSource, fluxSink, encoder, decoder);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ extern command_cb mainGetFile;
|
|||||||
extern command_cb mainGetFileInfo;
|
extern command_cb mainGetFileInfo;
|
||||||
extern command_cb mainInspect;
|
extern command_cb mainInspect;
|
||||||
extern command_cb mainLs;
|
extern command_cb mainLs;
|
||||||
|
extern command_cb mainPutFile;
|
||||||
extern command_cb mainRawRead;
|
extern command_cb mainRawRead;
|
||||||
extern command_cb mainRawWrite;
|
extern command_cb mainRawWrite;
|
||||||
extern command_cb mainRead;
|
extern command_cb mainRead;
|
||||||
@@ -40,6 +41,7 @@ static std::vector<Command> commands =
|
|||||||
{ "ls", mainLs, "Show files on disk (or image).", },
|
{ "ls", mainLs, "Show files on disk (or image).", },
|
||||||
{ "getfile", mainGetFile, "Read a file off a disk (or image).", },
|
{ "getfile", mainGetFile, "Read a file off a disk (or image).", },
|
||||||
{ "getfileinfo", mainGetFileInfo, "Read file metadata 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.", },
|
{ "rpm", mainRpm, "Measures the disk rotational speed.", },
|
||||||
{ "seek", mainSeek, "Moves the disk head.", },
|
{ "seek", mainSeek, "Moves the disk head.", },
|
||||||
{ "test", mainTest, "Various testing commands.", },
|
{ "test", mainTest, "Various testing commands.", },
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ void MainWindow::OnWriteFluxButton(wxCommandEvent&)
|
|||||||
decoder = AbstractDecoder::create(config.decoder());
|
decoder = AbstractDecoder::create(config.decoder());
|
||||||
fluxSource = FluxSource::create(config.flux_source());
|
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)
|
catch (const ErrorException& e)
|
||||||
|
|||||||
Reference in New Issue
Block a user