Merge pull request #574 from davidgiven/vfs

Dramatic GUI overhaul and addition of the new file browser.
This commit is contained in:
David Given
2022-09-10 01:18:43 +02:00
committed by GitHub
63 changed files with 5565 additions and 700 deletions

View File

@@ -41,7 +41,7 @@ AR ?= $(CCPREFIX)ar
PKG_CONFIG ?= pkg-config
WX_CONFIG ?= wx-config
PROTOC ?= protoc
CFLAGS ?= -g -O3
CFLAGS ?= -g -O0
CXXFLAGS += -std=c++17
LDFLAGS ?=
PLATFORM ?= UNIX

View File

@@ -4,10 +4,9 @@ FluxEngine
(If you're reading this on GitHub, the formatting's a bit messed up. [Try the
version on cowlark.com instead.](http://cowlark.com/fluxengine/))
**Breaking news!** As of 2021-05-21, the command line environment has changed
_substantially_ (to make it more consistent and flexible, and allow some new
backend features like multi-format IBM scheme disks, which are popular with
CP/M). If things don't work the way you expect, please check the documentation.
**Breaking news!** As of 2022-09-09, there's new filesystem support. Read (and
sometimes write) files directly from (and to) your disks! It works in the GUI,
too. See the details below.
What?
-----
@@ -67,6 +66,10 @@ following friendly articles:
- [Configuring for your drive](doc/drives.md) ∾ but I don't have a 80 track
drive! ∾ reading and writing 40 track disks ∾ Shugart and Apple II
- [Direct filesystem access](doc/filesystem.md) ∾ imaging files is a pain
∾ accessing files directly ∾ features and limitation ∾ it works on disk
images too, you say?
- [Troubleshooting dubious disks](doc/problems.md) ∾ it's not an exact
science ∾ the sector map ∾ clock detection and the histogram

View File

@@ -81,7 +81,7 @@ RETCODE adfRenameEntry(struct Volume *vol, SECTNUM pSect, char *oldName,
BOOL intl;
RETCODE rc;
if (strcmp(oldName,newName)==0)
if ((pSect == nPSect) && (strcmp(oldName,newName)==0))
return RC_OK;
intl = isINTL(vol->dosType) || isDIRCACHE(vol->dosType);

View File

@@ -2350,6 +2350,28 @@ static FRESULT dir_read (
/* Directory handling - Find an object in the directory */
/*-----------------------------------------------------------------------*/
static int compare_filenames(const char* s1, const char* s2, size_t n)
{
#if FF_CASE_INSENSITIVE_COMPARISONS
while (n--)
{
char c1 = *s1++;
char c2 = *s2++;
int d;
if (IsLower(c1)) c1 -= 0x20; /* To upper ASCII char */
if (IsLower(c2)) c2 -= 0x20; /* To upper ASCII char */
d = c1 - c2;
if (d)
return d;
}
return 0;
#else
return memcmp(s1, s2, n);
#endif
}
static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */
DIR* dp /* Pointer to the directory object with the file name */
)
@@ -2409,13 +2431,13 @@ static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */
}
} else { /* An SFN entry is found */
if (ord == 0 && sum == sum_sfn(dp->dir)) break; /* LFN matched? */
if (!(dp->fn[NSFLAG] & NS_LOSS) && !memcmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */
if (!(dp->fn[NSFLAG] & NS_LOSS) && !compare_filenames(dp->dir, dp->fn, 11)) break; /* SFN matched? */
ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */
}
}
#else /* Non LFN configuration */
dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK;
if (!(dp->dir[DIR_Attr] & AM_VOL) && !memcmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */
if (!(dp->dir[DIR_Attr] & AM_VOL) && !compare_filenames(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */
#endif
res = dir_next(dp, 0); /* Next entry */
} while (res == FR_OK);
@@ -2979,9 +3001,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr
sfn[i++] = d;
} else { /* SBC */
if (strchr("*+,:;<=>[]|\"\?\x7F", (int)c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */
#if FF_ALLOW_MIXED_CASE == 0
if (IsLower(c)) c -= 0x20; /* To upper */
#endif
sfn[i++] = c;
}
}

View File

@@ -162,9 +162,9 @@
*/
#define FF_ALLOW_MIXED_CASE 1
/* This option doesn't uppercase filenames before processing, in case the disk has
* low case filenames in it. */
#define FF_CASE_INSENSITIVE_COMPARISONS 1
/* This option ensures that SFNs on disk are compared using case-insensitive routines,
* some FAT variants store lower case filenames. dtrg */
/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations

BIN
doc/filebrowser.jpg Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

89
doc/filesystem.md Normal file
View File

@@ -0,0 +1,89 @@
Direct filesystem access
========================
FluxEngine will let you read and write files directly, in many cases. This will
allow you to pull files directly off a disk, or flux image, or disk image,
without needing to image it first. You can also write, and make other
modifications, including formatting blank disks, if the filesystem supports it.
The GUI even has a reasonably competent file manager.
<div style="text-align: center">
<a href="filebrowser.jpg"><img src="filebrowser.jpg" style="width:80%" alt="screenshot of the GUI in action"></a>
</div>
The following file systems are supported so far.
| Filesystem | Read? | Write? | Notes |
|:-----------------------------------------|:-----:|:------:|-------|
| Acorn DFS | Y | | |
| Amiga FFS | Y | Y | Both OFS and FFS |
| Brother 120kB | Y | | |
| Commodore CbmFS | Y | | Only 1541 disks so far |
| CP/M | Y | | For selected formats |
| FatFS (a.k.a. MS-DOS) | Y | Y | FAT12, FAT16, FAT32, but only SFN; not Atari |
| Macintosh HFS | Y | Y | Only AppleDouble files may be written |
Please not that Atari disks do _not_ use standard FatFS, and the library I'm
using for the heavy lifting (ChaN's
[FatFS](http://elm-chan.org/fsw/ff/00index_e.html) doesn't seem to support it.
If you know different, please [get in
touch](https://github.com/davidgiven/fluxengine/issues/new).
Using it
--------
To use, try syntax like this:
```
fluxengine ls ibm180 -f drive:0
```
`ibm180` is the format, which selects the most common filesystem automatically.
`-f drive:0` specifies a flux source/sink, in this case a real disk. You may
also specify a flux file (read only). Disk images may be specified with `-i
disk.img` (read/write).
Commands which take filename paramaters typically use `-p` to indicate the path
on the disk, and `-l` for the local filename. For example:
```
fluxengine putfile ibm180 -f drive:0 -p ondisk.pcx -l z.pcx
```
This will copy the file `z.pcx` onto the disk and call it `ONDISK.PCX`.
The directory separator character is _always_ `/`, even if you're using
Macintosh HFS, where it's usually `:`.
The command line interface is pretty clunky, and is intended for scripts. For
everyday use, I'd suggest using the GUI (see the screenshot above).
The supported commands are:
- **ls**: list files (including in a directory)
- **getfile**: pull a file off a disk
- **putfile**: put a file onto a disk
- **format**: create a new filesystem and format a disk
- **getfileinfo**: retrieves metadata about a file.
- **getdiskinfo**: retrieves metadata about the file system.
- **rm**: deletes a file or empty directory
- **mv**: renames a file (use `--path` and `--path2` for the old and new paths)
- **mkdir**: creates a directory
There are commands missing here; this is all a work in progress.
Overriding the filesystem type
------------------------------
Sometimes you might want to use a different filesystem on the disk than the
default; for example, Macintosh HFS filesystems are common on 3.5" floppies. You
can do this as follows:
```
fluxengine format ibm1440 -f drive:1 --filesystem.machfs=
```
Some filesystems won't work on some disks --- don't try this with Amiga FFS, for
example.

View File

@@ -6,6 +6,7 @@
#include "usb/usb.h"
#include "fluxsink/fluxsink.h"
#include "lib/fluxsink/fluxsink.pb.h"
#include "lib/readerwriter.cc"
#include "fmt/format.h"
class HardwareFluxSink : public FluxSink
@@ -14,32 +15,8 @@ public:
HardwareFluxSink(const HardwareFluxSinkProto& conf):
_config(conf)
{
if (config.drive().has_hard_sector_count())
{
int retries = 5;
usbSetDrive(config.drive().drive(), config.drive().high_density(), config.drive().index_mode());
nanoseconds_t oneRevolution = config.drive().rotational_period_ms() * 1e6;
if (oneRevolution == 0)
{
Logger() << BeginSpeedOperationLogMessage();
do {
oneRevolution = usbGetRotationalPeriod(config.drive().hard_sector_count());
_hardSectorThreshold = oneRevolution * 3 / (4 * config.drive().hard_sector_count());
retries--;
} while ((oneRevolution == 0) && (retries > 0));
config.mutable_drive()->set_rotational_period_ms(oneRevolution / 1e6);
Logger() << EndSpeedOperationLogMessage { oneRevolution };
}
if (oneRevolution == 0) {
Error() << "Failed\nIs a disk in the drive?";
}
}
else
_hardSectorThreshold = 0;
nanoseconds_t oneRevolution;
measureDiskRotation(oneRevolution, _hardSectorThreshold);
}
~HardwareFluxSink()

View File

@@ -6,6 +6,7 @@
#include "usb/usb.h"
#include "fluxsource/fluxsource.h"
#include "lib/fluxsource/fluxsource.pb.h"
#include "lib/readerwriter.h"
#include "fmt/format.h"
class HardwareFluxSource : public FluxSource
@@ -50,35 +51,7 @@ private:
public:
HardwareFluxSource(const HardwareFluxSourceProto& conf): _config(conf)
{
int retries = 5;
usbSetDrive(config.drive().drive(), config.drive().high_density(), config.drive().index_mode());
Logger() << BeginSpeedOperationLogMessage();
_oneRevolution = config.drive().rotational_period_ms() * 1e6;
_hardSectorThreshold = 0;
if (_oneRevolution == 0)
{
Logger() << BeginOperationLogMessage{"Measuring drive rotational speed"};
do
{
_oneRevolution =
usbGetRotationalPeriod(config.drive().hard_sector_count());
if (config.drive().hard_sector_count() != 0)
_hardSectorThreshold =
_oneRevolution * 3 / (4 * config.drive().hard_sector_count());
else
_hardSectorThreshold = 0;
retries--;
} while ((_oneRevolution == 0) && (retries > 0));
config.mutable_drive()->set_rotational_period_ms(_oneRevolution / 1e6);
Logger() << EndOperationLogMessage{};
}
if (_oneRevolution == 0)
Error() << "Failed\nIs a disk in the drive?";
Logger() << EndSpeedOperationLogMessage{_oneRevolution};
measureDiskRotation(_oneRevolution, _hardSectorThreshold);
}
~HardwareFluxSource() {}

View File

@@ -34,6 +34,8 @@ extern void hexdumpForSrp16(std::ostream& stream, const Bytes& bytes);
struct ErrorException
{
ErrorException(const std::string& message): message(message){}
const std::string message;
void print() const;

View File

@@ -1,75 +1,110 @@
#include "globals.h"
#include "sector.h"
#include "image.h"
#include "layout.h"
Image::Image()
{}
Image::Image() {}
Image::Image(std::set<std::shared_ptr<const Sector>>& sectors)
{
for (auto& sector : sectors)
{
key_t key = std::make_tuple(sector->logicalTrack, sector->logicalSide, sector->logicalSector);
_sectors[key] = sector;
}
calculateSize();
for (auto& sector : sectors)
{
key_t key = std::make_tuple(
sector->logicalTrack, sector->logicalSide, sector->logicalSector);
_sectors[key] = sector;
}
calculateSize();
}
void Image::clear()
{
_sectors.clear();
_geometry = {0, 0, 0};
}
void Image::createBlankImage()
{
clear();
for (const auto& trackAndHead : Layout::getTrackOrdering())
{
unsigned track = trackAndHead.first;
unsigned side = trackAndHead.second;
auto layout = Layout::getLayoutOfTrack(track, side);
Bytes blank(layout.sector_size());
for (unsigned sectorId : Layout::getSectorsInTrack(layout))
put(track, side, sectorId)->data = blank;
}
}
bool Image::empty() const
{
return _sectors.empty();
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();
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;
key_t key = std::make_tuple(track, side, sectorid);
auto i = _sectors.find(key);
if (i == _sectors.end())
return NONE;
return i->second;
key_t key = std::make_tuple(track, side, sectorid);
auto i = _sectors.find(key);
if (i == _sectors.end())
return NONE;
return i->second;
}
std::shared_ptr<Sector> Image::put(unsigned track, unsigned side, unsigned sectorid)
std::shared_ptr<Sector> Image::put(
unsigned track, unsigned side, unsigned sectorid)
{
key_t key = std::make_tuple(track, side, sectorid);
std::shared_ptr<Sector> sector = std::make_shared<Sector>();
sector->logicalTrack = track;
sector->logicalSide = side;
sector->logicalSector = sectorid;
_sectors[key] = sector;
return sector;
key_t key = std::make_tuple(track, side, sectorid);
std::shared_ptr<Sector> sector = std::make_shared<Sector>();
sector->logicalTrack = track;
sector->logicalSide = side;
sector->logicalSector = sectorid;
_sectors[key] = sector;
return sector;
}
void Image::erase(unsigned track, unsigned side, unsigned sectorid)
{
key_t key = std::make_tuple(track, side, sectorid);
_sectors.erase(key);
key_t key = std::make_tuple(track, side, sectorid);
_sectors.erase(key);
}
std::set<std::pair<unsigned, unsigned>> Image::tracks() const
{
std::set<std::pair<unsigned, unsigned>> result;
for (const auto& e : _sectors)
result.insert(
std::make_pair(std::get<0>(e.first), std::get<1>(e.first)));
return result;
}
void Image::calculateSize()
{
_geometry = {};
unsigned maxSector = 0;
for (const auto& i : _sectors)
{
const auto& sector = i.second;
if (sector)
{
_geometry.numTracks = std::max(_geometry.numTracks, (unsigned)sector->logicalTrack+1);
_geometry.numSides = std::max(_geometry.numSides, (unsigned)sector->logicalSide+1);
_geometry.firstSector = std::min(_geometry.firstSector, (unsigned)sector->logicalSector);
maxSector = std::max(maxSector, (unsigned)sector->logicalSector);
_geometry.sectorSize = std::max(_geometry.sectorSize, (unsigned)sector->data.size());
}
}
_geometry.numSectors = maxSector - _geometry.firstSector + 1;
_geometry = {};
unsigned maxSector = 0;
for (const auto& i : _sectors)
{
const auto& sector = i.second;
if (sector)
{
_geometry.numTracks = std::max(
_geometry.numTracks, (unsigned)sector->logicalTrack + 1);
_geometry.numSides =
std::max(_geometry.numSides, (unsigned)sector->logicalSide + 1);
_geometry.firstSector = std::min(
_geometry.firstSector, (unsigned)sector->logicalSector);
maxSector = std::max(maxSector, (unsigned)sector->logicalSector);
_geometry.sectorSize =
std::max(_geometry.sectorSize, (unsigned)sector->data.size());
}
}
_geometry.numSectors = maxSector - _geometry.firstSector + 1;
}

View File

@@ -3,58 +3,91 @@
struct Geometry
{
unsigned numTracks = 0;
unsigned numSides = 0;
unsigned firstSector = UINT_MAX;
unsigned numSectors = 0;
unsigned sectorSize = 0;
bool irregular = false;
unsigned numTracks = 0;
unsigned numSides = 0;
unsigned firstSector = UINT_MAX;
unsigned numSectors = 0;
unsigned sectorSize = 0;
bool irregular = false;
};
class Image
{
private:
typedef std::tuple<unsigned, unsigned, unsigned> key_t;
typedef std::tuple<unsigned, unsigned, unsigned> key_t;
public:
Image();
Image(std::set<std::shared_ptr<const Sector>>& sectors);
Image();
Image(std::set<std::shared_ptr<const Sector>>& sectors);
public:
class const_iterator
{
typedef std::map<key_t, std::shared_ptr<const Sector>>::const_iterator wrapped_iterator_t;
class const_iterator
{
typedef std::map<key_t, std::shared_ptr<const Sector>>::const_iterator
wrapped_iterator_t;
public:
const_iterator(const wrapped_iterator_t& it): _it(it) {}
std::shared_ptr<const Sector> operator* () { return _it->second; }
void operator++ () { _it++; }
bool operator== (const const_iterator& other) const { return _it == other._it; }
bool operator!= (const const_iterator& other) const { return _it != other._it; }
public:
const_iterator(const wrapped_iterator_t& it): _it(it) {}
std::shared_ptr<const Sector> operator*()
{
return _it->second;
}
private:
wrapped_iterator_t _it;
};
void operator++()
{
_it++;
}
bool operator==(const const_iterator& other) const
{
return _it == other._it;
}
bool operator!=(const const_iterator& other) const
{
return _it != other._it;
}
private:
wrapped_iterator_t _it;
};
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<Sector> put(unsigned track, unsigned side, unsigned sectorId);
void erase(unsigned track, unsigned side, unsigned sectorId);
void clear();
void createBlankImage();
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()); }
std::set<std::pair<unsigned, unsigned>> tracks() const;
void setGeometry(Geometry geometry) { _geometry = geometry; }
const Geometry& getGeometry() const { return _geometry; }
const_iterator begin() const
{
return const_iterator(_sectors.cbegin());
}
const_iterator end() const
{
return const_iterator(_sectors.cend());
}
void setGeometry(Geometry geometry)
{
_geometry = geometry;
}
const Geometry& getGeometry() const
{
return _geometry;
}
private:
Geometry _geometry = {0, 0, 0};
std::map<key_t, std::shared_ptr<const Sector>> _sectors;
Geometry _geometry = {0, 0, 0};
std::map<key_t, std::shared_ptr<const Sector>> _sectors;
};
#endif

View File

@@ -130,46 +130,32 @@ std::set<Location> Mapper::computeLocations()
{
std::set<Location> locations;
unsigned track_step = getTrackStep();
for (unsigned logicalTrack : iterate(config.tracks()))
{
for (unsigned head : iterate(config.heads()))
{
unsigned physicalTrack =
config.drive().head_bias() + logicalTrack * track_step;
locations.insert({.physicalTrack = physicalTrack,
.logicalTrack = logicalTrack,
.head = head,
.groupSize = track_step});
}
locations.insert(computeLocationFor(logicalTrack, head));
}
return locations;
}
Location Mapper::computeLocationFor(unsigned desiredTrack, unsigned desiredHead)
Location Mapper::computeLocationFor(unsigned logicalTrack, unsigned logicalHead)
{
unsigned track_step = getTrackStep();
for (unsigned logicalTrack : iterate(config.tracks()))
if ((logicalTrack < config.layout().tracks()) &&
(logicalHead < config.layout().sides()))
{
for (unsigned head : iterate(config.heads()))
{
if ((logicalTrack == desiredTrack) && (head == desiredHead))
{
unsigned physicalTrack =
config.drive().head_bias() + logicalTrack * track_step;
unsigned track_step = getTrackStep();
unsigned physicalTrack =
config.drive().head_bias() + logicalTrack * track_step;
return {.physicalTrack = physicalTrack,
.logicalTrack = logicalTrack,
.head = head,
.groupSize = track_step};
}
}
return {.physicalTrack = physicalTrack,
.logicalTrack = logicalTrack,
.head = logicalHead,
.groupSize = track_step};
}
Error() << fmt::format(
"track {}.{} is not part of the image", desiredTrack, desiredHead);
"track {}.{} is not part of the image", logicalTrack, logicalHead);
}
nanoseconds_t Mapper::calculatePhysicalClockPeriod(

View File

@@ -56,6 +56,46 @@ private:
_cache;
};
void measureDiskRotation(
nanoseconds_t& oneRevolution, nanoseconds_t& hardSectorThreshold)
{
Logger() << BeginSpeedOperationLogMessage();
int retries = 5;
usbSetDrive(config.drive().drive(),
config.drive().high_density(),
config.drive().index_mode());
oneRevolution = config.drive().rotational_period_ms() * 1e6;
if (config.drive().hard_sector_count() != 0)
hardSectorThreshold =
oneRevolution * 3 / (4 * config.drive().hard_sector_count());
else
hardSectorThreshold = 0;
if (oneRevolution == 0)
{
Logger() << BeginOperationLogMessage{
"Measuring drive rotational speed"};
do
{
oneRevolution =
usbGetRotationalPeriod(config.drive().hard_sector_count());
if (config.drive().hard_sector_count() != 0)
hardSectorThreshold = oneRevolution * 3 /
(4 * config.drive().hard_sector_count());
retries--;
} while ((oneRevolution == 0) && (retries > 0));
config.mutable_drive()->set_rotational_period_ms(oneRevolution / 1e6);
Logger() << EndOperationLogMessage{};
}
if (oneRevolution == 0)
Error() << "Failed\nIs a disk in the drive?";
Logger() << EndSpeedOperationLogMessage{oneRevolution};
}
/* Given a set of sectors, deduplicates them sensibly (e.g. if there is a good
* and bad version of the same sector, the bad version is dropped). */
@@ -185,12 +225,11 @@ ReadResult readGroup(FluxSourceIteratorHolder& fluxSourceIteratorHolder,
void writeTracks(FluxSink& fluxSink,
std::function<std::unique_ptr<const Fluxmap>(const Location& location)>
producer,
std::function<bool(const Location& location)> verifier)
std::function<bool(const Location& location)> verifier,
const std::set<Location>& locations)
{
Logger() << BeginOperationLogMessage{"Encoding and writing to disk"};
auto locations = Mapper::computeLocations();
int index = 0;
for (const auto& location : locations)
{
@@ -255,8 +294,10 @@ static bool dontVerify(const Location&)
return true;
}
void writeTracks(
FluxSink& fluxSink, AbstractEncoder& encoder, const Image& image)
void writeTracks(FluxSink& fluxSink,
AbstractEncoder& encoder,
const Image& image,
const std::set<Location>& locations)
{
writeTracks(
fluxSink,
@@ -265,14 +306,16 @@ void writeTracks(
auto sectors = encoder.collectSectors(location, image);
return encoder.encode(location, sectors, image);
},
dontVerify);
dontVerify,
locations);
}
void writeTracksAndVerify(FluxSink& fluxSink,
AbstractEncoder& encoder,
FluxSource& fluxSource,
AbstractDecoder& decoder,
const Image& image)
const Image& image,
const std::set<Location>& locations)
{
writeTracks(
fluxSink,
@@ -329,14 +372,16 @@ void writeTracksAndVerify(FluxSink& fluxSink,
return false;
}
return true;
});
},
locations);
}
void writeDiskCommand(const Image& image,
AbstractEncoder& encoder,
FluxSink& fluxSink,
AbstractDecoder* decoder,
FluxSource* fluxSource)
FluxSource* fluxSource,
const std::set<Location>& locations)
{
const Image* imagep = &image;
std::unique_ptr<const Image> remapped;
@@ -348,9 +393,20 @@ void writeDiskCommand(const Image& image,
}
if (fluxSource && decoder)
writeTracksAndVerify(fluxSink, encoder, *fluxSource, *decoder, *imagep);
writeTracksAndVerify(
fluxSink, encoder, *fluxSource, *decoder, *imagep, locations);
else
writeTracks(fluxSink, encoder, *imagep);
writeTracks(fluxSink, encoder, *imagep, locations);
}
void writeDiskCommand(const Image& image,
AbstractEncoder& encoder,
FluxSink& fluxSink,
AbstractDecoder* decoder,
FluxSource* fluxSource)
{
auto locations = Mapper::computeLocations();
writeDiskCommand(image, encoder, fluxSink, decoder, fluxSource, locations);
}
void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink)
@@ -362,7 +418,8 @@ void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink)
return fluxSource.readFlux(location.physicalTrack, location.head)
->next();
},
dontVerify);
dontVerify,
Mapper::computeLocations());
}
std::shared_ptr<TrackFlux> readAndDecodeTrack(

View File

@@ -14,15 +14,33 @@ class ImageWriter;
class Location;
class TrackFlux;
extern void measureDiskRotation(
nanoseconds_t& oneRevolution, nanoseconds_t& hardSectorThreshold);
extern void writeTracks(FluxSink& fluxSink,
const std::function<std::unique_ptr<const Fluxmap>(
const Location& location)> producer);
const Location& location)> producer,
const std::set<Location>& locations);
extern void writeTracksAndVerify(FluxSink& fluxSink,
AbstractEncoder& encoder,
FluxSource& fluxSource,
AbstractDecoder& decoder,
const Image& image,
const std::set<Location>& locations);
extern void fillBitmapTo(std::vector<bool>& bitmap,
unsigned& cursor,
unsigned terminateAt,
const std::vector<bool>& pattern);
extern void writeDiskCommand(const Image& image,
AbstractEncoder& encoder,
FluxSink& fluxSink,
AbstractDecoder* decoder,
FluxSource* fluxSource,
const std::set<Location>& locations);
extern void writeDiskCommand(const Image& image,
AbstractEncoder& encoder,
FluxSink& fluxSink,
@@ -31,9 +49,8 @@ extern void writeDiskCommand(const Image& image,
extern void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink);
extern std::shared_ptr<TrackFlux> readAndDecodeTrack(FluxSource& fluxSource,
AbstractDecoder& decoder,
const Location& location);
extern std::shared_ptr<TrackFlux> readAndDecodeTrack(
FluxSource& fluxSource, AbstractDecoder& decoder, const Location& location);
extern std::shared_ptr<const DiskFlux> readDiskCommand(
FluxSource& fluxsource, AbstractDecoder& decoder);

View File

@@ -74,6 +74,14 @@ bool endsWith(const std::string& value, const std::string& ending)
std::equal(ending.rbegin(), ending.rend(), lowercase.begin());
}
std::string toUpper(const std::string& value)
{
std::string s = value;
for (char& c : s)
c = toupper(c);
return s;
}
std::string leftTrimWhitespace(std::string value)
{
value.erase(0, value.find_first_not_of(WHITESPACE));

View File

@@ -9,6 +9,7 @@ extern std::vector<std::string> split(
const std::string& string, char separator);
extern bool beginsWith(const std::string& value, const std::string& beginning);
extern bool endsWith(const std::string& value, const std::string& ending);
extern std::string toUpper(const std::string& value);
extern std::string leftTrimWhitespace(std::string value);
extern std::string rightTrimWhitespace(std::string value);
extern std::string trimWhitespace(const std::string& value);

View File

@@ -18,6 +18,7 @@ public:
filename = filename.substr(0, filename.find(' '));
this->inode = inode;
path = {filename};
start_sector = ((bytes1[6] & 0x03) << 8) | bytes1[7];
load_address =
((bytes1[6] & 0x0c) << 14) | (bytes1[1] << 8) | bytes1[0];
@@ -28,6 +29,18 @@ public:
sector_count = (length + 255) / 256;
file_type = TYPE_FILE;
mode = locked ? "L" : "";
attributes[Filesystem::FILENAME] = filename;
attributes[Filesystem::LENGTH] = fmt::format("{}", length);
attributes[Filesystem::FILE_TYPE] = "file";
attributes[Filesystem::MODE] = mode;
attributes["acorndfs.inode"] = fmt::format("{}", inode);
attributes["acorndfs.start_sector"] = fmt::format("{}", start_sector);
attributes["acorndfs.load_address"] =
fmt::format("0x{:x}", load_address);
attributes["acorndfs.exec_address"] =
fmt::format("0x{:x}", exec_address);
attributes["acorndfs.locked"] = fmt::format("{}", locked);
}
public:
@@ -101,6 +114,11 @@ public:
{
}
uint32_t capabilities() const
{
return OP_GETFSDATA | OP_LIST | OP_GETFILE | OP_GETDIRENT;
}
std::map<std::string, std::string> getMetadata()
{
AcornDfsDirectory dir(this);
@@ -149,26 +167,10 @@ public:
return data;
}
std::map<std::string, std::string> getMetadata(const Path& path)
std::shared_ptr<Dirent> getDirent(const Path& path)
{
std::map<std::string, std::string> attributes;
AcornDfsDirectory dir(this);
auto dirent = dir.findFile(path);
attributes[FILENAME] = dirent->filename;
attributes[LENGTH] = fmt::format("{}", dirent->length);
attributes[FILE_TYPE] = "file";
attributes[MODE] = dirent->mode;
attributes["acorndfs.inode"] = fmt::format("{}", dirent->inode);
attributes["acorndfs.start_sector"] =
fmt::format("{}", dirent->start_sector);
attributes["acorndfs.load_address"] =
fmt::format("0x{:x}", dirent->load_address);
attributes["acorndfs.exec_address"] =
fmt::format("0x{:x}", dirent->exec_address);
attributes["acorndfs.locked"] = fmt::format("{}", dirent->locked);
return attributes;
return dir.findFile(path);
}
private:

View File

@@ -3,6 +3,7 @@
#include "lib/config.pb.h"
#include "lib/proto.h"
#include "lib/layout.h"
#include "lib/logger.h"
#include <fmt/format.h>
#include "adflib.h"
@@ -56,7 +57,13 @@ public:
{
}
std::map<std::string, std::string> getMetadata()
uint32_t capabilities() const
{
return OP_GETFSDATA | OP_CREATE | OP_LIST | OP_GETFILE | OP_PUTFILE |
OP_GETDIRENT | OP_DELETE | OP_MOVE | OP_CREATEDIR;
}
std::map<std::string, std::string> getMetadata() override
{
AdfMount m(this);
auto* vol = m.mount();
@@ -72,7 +79,7 @@ public:
return attributes;
}
void create(bool quick, const std::string& volumeName)
void create(bool quick, const std::string& volumeName) override
{
if (!quick)
eraseEverythingOnDisk();
@@ -92,12 +99,12 @@ public:
throw CannotWriteException();
}
FilesystemStatus check()
FilesystemStatus check() override
{
return FS_OK;
}
std::vector<std::shared_ptr<Dirent>> list(const Path& path)
std::vector<std::shared_ptr<Dirent>> list(const Path& path) override
{
AdfMount m(this);
@@ -113,19 +120,13 @@ public:
auto* entry = (struct Entry*)cell->content;
cell = cell->next;
auto dirent = std::make_shared<Dirent>();
dirent->filename = entry->name;
dirent->length = entry->size;
dirent->file_type =
(entry->type == ST_FILE) ? TYPE_FILE : TYPE_DIRECTORY;
dirent->mode = modeToString(entry->access);
results.push_back(dirent);
results.push_back(toDirent(entry, path));
}
return results;
}
std::map<std::string, std::string> getMetadata(const Path& path)
std::shared_ptr<Dirent> getDirent(const Path& path) override
{
AdfMount m(this);
if (path.size() == 0)
@@ -136,29 +137,12 @@ public:
auto entry = AdfEntry(adfFindEntry(vol, (char*)path.back().c_str()));
if (!entry)
throw BadPathException();
throw FileNotFoundException();
std::map<std::string, std::string> attributes;
attributes[FILENAME] = entry->name;
attributes[LENGTH] = fmt::format("{}", entry->size);
attributes[FILE_TYPE] = (entry->type == ST_FILE) ? "file" : "dir";
attributes[MODE] = modeToString(entry->access);
attributes["amigaffs.comment"] = entry->comment;
attributes["amigaffs.sector"] = entry->sector;
std::tm tm = {.tm_sec = entry->secs,
.tm_min = entry->mins,
.tm_hour = entry->hour,
.tm_mday = entry->days,
.tm_mon = entry->month,
.tm_year = entry->year - 1900};
std::stringstream ss;
ss << std::put_time(&tm, "%FT%T%z");
attributes["amigaffs.mtime"] = ss.str();
return attributes;
return toDirent(entry, path.parent());
}
Bytes getFile(const Path& path)
Bytes getFile(const Path& path) override
{
AdfMount m(this);
if (path.size() == 0)
@@ -185,7 +169,7 @@ public:
return bytes;
}
void putFile(const Path& path, const Bytes& data)
void putFile(const Path& path, const Bytes& data) override
{
AdfMount m(this);
if (path.size() == 0)
@@ -209,7 +193,90 @@ public:
adfCloseFile(file);
}
void deleteFile(const Path& path) override
{
AdfMount m(this);
if (path.size() == 0)
throw BadPathException();
auto* vol = m.mount();
changeDirButOne(vol, path);
int res =
adfRemoveEntry(vol, vol->curDirPtr, (char*)path.back().c_str());
if (res != RC_OK)
throw CannotWriteException();
}
void moveFile(const Path& oldPath, const Path& newPath) override
{
AdfMount m(this);
if ((oldPath.size() == 0) || (newPath.size() == 0))
throw BadPathException();
auto* vol = m.mount();
changeDirButOne(vol, oldPath);
auto oldDir = vol->curDirPtr;
changeDirButOne(vol, newPath);
auto newDir = vol->curDirPtr;
int res = adfRenameEntry(vol,
oldDir,
(char*)oldPath.back().c_str(),
newDir,
(char*)newPath.back().c_str());
if (res != RC_OK)
throw CannotWriteException();
}
void createDirectory(const Path& path)
{
AdfMount m(this);
if (path.empty())
throw BadPathException();
auto* vol = m.mount();
changeDirButOne(vol, path);
int res = adfCreateDir(vol, vol->curDirPtr, (char*)path.back().c_str());
if (res != RC_OK)
throw CannotWriteException();
}
private:
std::shared_ptr<Dirent> toDirent(struct Entry* entry, const Path& container)
{
auto dirent = std::make_shared<Dirent>();
dirent->path = container;
dirent->path.push_back(entry->name);
dirent->filename = entry->name;
dirent->length = entry->size;
dirent->file_type =
(entry->type == ST_FILE) ? TYPE_FILE : TYPE_DIRECTORY;
dirent->mode = modeToString(entry->access);
dirent->attributes[FILENAME] = entry->name;
dirent->attributes[LENGTH] = fmt::format("{}", entry->size);
dirent->attributes[FILE_TYPE] =
(entry->type == ST_FILE) ? "file" : "dir";
dirent->attributes[MODE] = modeToString(entry->access);
dirent->attributes["amigaffs.comment"] = entry->comment;
std::tm tm = {.tm_sec = entry->secs,
.tm_min = entry->mins,
.tm_hour = entry->hour,
.tm_mday = entry->days,
.tm_mon = entry->month,
.tm_year = entry->year - 1900};
std::stringstream ss;
ss << std::put_time(&tm, "%FT%T%z");
dirent->attributes["amigaffs.mtime"] = ss.str();
return dirent;
}
class AdfEntry
{
public:
@@ -274,6 +341,8 @@ private:
~AdfMount()
{
if (vol)
adfUnMount(vol);
if (self->_ffs)
adfUnMountDev(self->_ffs);
adfEnvCleanUp();
@@ -282,11 +351,17 @@ private:
struct Volume* mount()
{
self->_ffs = adfMountDev(nullptr, false);
return adfMount(self->_ffs, 0, false);
if (!self->_ffs)
throw BadFilesystemException();
vol = adfMount(self->_ffs, 0, false);
if (!vol)
throw BadFilesystemException();
return vol;
}
private:
AmigaFfsFilesystem* self;
struct Volume* vol = nullptr;
};
void changeDir(struct Volume* vol, const Path& path)
@@ -344,6 +419,16 @@ private:
struct Device* _ffs;
};
static void onAdfWarning(char* message)
{
Logger() << message;
}
static void onAdfError(char* message)
{
throw FilesystemException(message);
}
void adfInitNativeFct()
{
auto cbs = (struct nativeFunctions*)adfEnv.nativeFct;
@@ -352,6 +437,9 @@ void adfInitNativeFct()
cbs->adfNativeWriteSector = ::adfNativeWriteSector;
cbs->adfIsDevNative = ::adfIsDevNative;
cbs->adfReleaseDevice = ::adfReleaseDevice;
adfChgEnvProp(PR_WFCT, (void*)onAdfWarning);
adfChgEnvProp(PR_EFCT, (void*)onAdfError);
}
static RETCODE adfInitDevice(struct Device* dev, char* name, BOOL ro)

View File

@@ -32,6 +32,7 @@ public:
ByteReader br(bytes);
filename = br.read(8);
filename = filename.substr(0, filename.find(' '));
path = {filename};
this->inode = inode;
brother_type = br.read_8();
@@ -40,6 +41,14 @@ public:
length = sector_length * SECTOR_SIZE;
file_type = TYPE_FILE;
mode = "";
attributes[Filesystem::FILENAME] = filename;
attributes[Filesystem::LENGTH] = fmt::format("{}", length);
attributes[Filesystem::FILE_TYPE] = "file";
attributes[Filesystem::MODE] = mode;
attributes["brother120.inode"] = fmt::format("{}", inode);
attributes["brother120.start_sector"] = fmt::format("{}", start_sector);
attributes["brother120.type"] = fmt::format("{}", brother_type);
}
public:
@@ -68,7 +77,7 @@ public:
auto de = std::make_unique<Brother120Dirent>(inode, buffer);
usedSectors += de->sector_length;
// dirents.push_back(std::move(de));
dirents.push_back(std::move(de));
}
}
@@ -112,7 +121,12 @@ public:
{
}
std::map<std::string, std::string> getMetadata()
uint32_t capabilities() const
{
return OP_GETFSDATA | OP_LIST | OP_GETFILE | OP_GETDIRENT;
}
std::map<std::string, std::string> getMetadata() override
{
BrotherDirectory dir(this);
@@ -124,12 +138,12 @@ public:
return attributes;
}
FilesystemStatus check()
FilesystemStatus check() override
{
return FS_OK;
}
std::vector<std::shared_ptr<Dirent>> list(const Path& path)
std::vector<std::shared_ptr<Dirent>> list(const Path& path) override
{
if (!path.empty())
throw FileNotFoundException();
@@ -143,7 +157,7 @@ public:
return result;
}
Bytes getFile(const Path& path)
Bytes getFile(const Path& path) override
{
BrotherDirectory dir(this);
auto dirent = dir.findFile(path);
@@ -160,22 +174,10 @@ public:
return data;
}
std::map<std::string, std::string> getMetadata(const Path& path)
std::shared_ptr<Dirent> getDirent(const Path& path) override
{
std::map<std::string, std::string> attributes;
BrotherDirectory dir(this);
auto dirent = dir.findFile(path);
attributes[FILENAME] = dirent->filename;
attributes[LENGTH] = fmt::format("{}", dirent->length);
attributes[FILE_TYPE] = "file";
attributes[MODE] = dirent->mode;
attributes["brother120.inode"] = fmt::format("{}", dirent->inode);
attributes["brother120.start_sector"] =
fmt::format("{}", dirent->start_sector);
attributes["brother120.type"] = fmt::format("{}", dirent->brother_type);
return attributes;
return dir.findFile(path);
}
private:

View File

@@ -74,6 +74,7 @@ class CbmfsFilesystem : public Filesystem
auto filenameBytes = br.read(16).split(0xa0)[0];
filename = fromPetscii(filenameBytes);
path = {filename};
side_track = br.read_8();
side_sector = br.read_8();
recordlen = br.read_8();
@@ -83,6 +84,18 @@ class CbmfsFilesystem : public Filesystem
file_type = TYPE_FILE;
length = sectors * 254;
mode = "";
attributes[Filesystem::FILENAME] = filename;
attributes[Filesystem::LENGTH] = fmt::format("{}", length);
attributes[Filesystem::FILE_TYPE] = "file";
attributes[Filesystem::MODE] = mode;
attributes["cbmfs.type"] = toFileType(cbm_type);
attributes["cbmfs.start_track"] = fmt::format("{}", start_track);
attributes["cbmfs.start_sector"] = fmt::format("{}", start_sector);
attributes["cbmfs.side_track"] = fmt::format("{}", side_track);
attributes["cbmfs.side_sector"] = fmt::format("{}", side_sector);
attributes["cbmfs.recordlen"] = fmt::format("{}", recordlen);
attributes["cbmfs.sectors"] = fmt::format("{}", sectors);
}
public:
@@ -182,7 +195,12 @@ public:
{
}
std::map<std::string, std::string> getMetadata()
uint32_t capabilities() const
{
return OP_GETFSDATA | OP_LIST | OP_GETFILE | OP_GETDIRENT;
}
std::map<std::string, std::string> getMetadata() override
{
Directory dir(this);
@@ -196,12 +214,12 @@ public:
return attributes;
}
FilesystemStatus check()
FilesystemStatus check() override
{
return FS_OK;
}
std::vector<std::shared_ptr<Dirent>> list(const Path& path)
std::vector<std::shared_ptr<Dirent>> list(const Path& path) override
{
if (path.size() != 0)
throw BadPathException();
@@ -214,29 +232,16 @@ public:
return results;
}
std::map<std::string, std::string> getMetadata(const Path& path)
std::shared_ptr<Dirent> getDirent(const Path& path) override
{
if (path.size() != 1)
throw BadPathException();
Directory dir(this);
auto de = dir.findFile(unhex(path[0]));
std::map<std::string, std::string> attributes;
attributes[FILENAME] = de->filename;
attributes[LENGTH] = fmt::format("{}", de->length);
attributes[FILE_TYPE] = "file";
attributes[MODE] = de->mode;
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;
Directory dir(this);
return dir.findFile(unhex(path[0]));
}
Bytes getFile(const Path& path)
Bytes getFile(const Path& path) override
{
if (path.size() != 1)
throw BadPathException();

View File

@@ -82,7 +82,12 @@ public:
{
}
std::map<std::string, std::string> getMetadata()
uint32_t capabilities() const
{
return OP_GETFSDATA | OP_LIST | OP_GETFILE | OP_GETDIRENT;
}
std::map<std::string, std::string> getMetadata() override
{
mount();
@@ -108,12 +113,12 @@ public:
return attributes;
}
FilesystemStatus check()
FilesystemStatus check() override
{
return FS_OK;
}
std::vector<std::shared_ptr<Dirent>> list(const Path& path)
std::vector<std::shared_ptr<Dirent>> list(const Path& path) override
{
mount();
if (!path.empty())
@@ -130,6 +135,7 @@ public:
if (!dirent)
{
dirent = std::make_unique<Dirent>();
dirent->path = {entry->filename};
dirent->filename = entry->filename;
dirent->mode = entry->mode;
dirent->length = 0;
@@ -142,50 +148,33 @@ public:
std::vector<std::shared_ptr<Dirent>> result;
for (auto& e : map)
result.push_back(std::move(e.second));
{
auto& de = e.second;
de->attributes[FILENAME] = de->filename;
de->attributes[LENGTH] = fmt::format("{}", de->length);
de->attributes[FILE_TYPE] = "file";
de->attributes[MODE] = de->mode;
result.push_back(std::move(de));
}
return result;
}
std::map<std::string, std::string> getMetadata(const Path& path)
std::shared_ptr<Dirent> getDirent(const Path& path) override
{
mount();
if (path.size() != 1)
throw BadPathException();
std::shared_ptr<Dirent> dirent;
for (int d = 0; d < _config.dir_entries(); d++)
for (const auto& dirent : list(Path()))
{
auto entry = getEntry(d);
if (!entry)
continue;
if (path[0] != entry->filename)
continue;
if (!dirent)
{
dirent = std::make_shared<Dirent>();
dirent->filename = entry->filename;
dirent->mode = entry->mode;
dirent->length = 0;
dirent->file_type = TYPE_FILE;
}
dirent->length = std::max(
dirent->length, entry->extent * 16384 + entry->records * 128);
if (dirent->filename == path.front())
return dirent;
}
if (!dirent)
throw FileNotFoundException();
std::map<std::string, std::string> attributes;
attributes[FILENAME] = dirent->filename;
attributes[LENGTH] = fmt::format("{}", dirent->length);
attributes[FILE_TYPE] = "file";
attributes[MODE] = dirent->mode;
return attributes;
throw FileNotFoundException();
}
Bytes getFile(const Path& path)
Bytes getFile(const Path& path) override
{
mount();
if (path.size() != 1)

View File

@@ -1,6 +1,7 @@
#include "lib/globals.h"
#include "lib/vfs/vfs.h"
#include "lib/config.pb.h"
#include "lib/utils.h"
#include <fmt/format.h>
extern "C"
@@ -36,7 +37,13 @@ public:
{
}
std::map<std::string, std::string> getMetadata()
uint32_t capabilities() const
{
return OP_GETFSDATA | OP_CREATE | OP_LIST | OP_GETFILE | OP_PUTFILE |
OP_GETDIRENT | OP_MOVE | OP_CREATEDIR | OP_DELETE;
}
std::map<std::string, std::string> getMetadata() override
{
mount();
@@ -64,31 +71,34 @@ public:
return attributes;
}
void create(bool quick, const std::string& volumeName)
void create(bool quick, const std::string& volumeName) override
{
if (!quick)
eraseEverythingOnDisk();
char buffer[FF_MAX_SS * 2];
currentFatFs = this;
FRESULT res = f_mkfs("", nullptr, buffer, sizeof(buffer));
MKFS_PARM parm = {
.fmt = FM_SFD | FM_ANY,
};
FRESULT res = f_mkfs("", &parm, buffer, sizeof(buffer));
throwError(res);
mount();
f_setlabel(volumeName.c_str());
}
FilesystemStatus check()
FilesystemStatus check() override
{
return FS_OK;
}
std::vector<std::shared_ptr<Dirent>> list(const Path& path)
std::vector<std::shared_ptr<Dirent>> list(const Path& path) override
{
mount();
DIR dir;
auto pathstr = path.to_str();
auto pathstr = toUpper(path.to_str());
FRESULT res = f_opendir(&dir, pathstr.c_str());
std::vector<std::shared_ptr<Dirent>> results;
@@ -101,41 +111,30 @@ public:
if (filinfo.fname[0] == 0)
break;
auto dirent = std::make_shared<Dirent>();
dirent->filename = filinfo.fname;
dirent->length = filinfo.fsize;
dirent->file_type =
(filinfo.fattrib & AM_DIR) ? TYPE_DIRECTORY : TYPE_FILE;
dirent->mode = modeToString(filinfo.fattrib);
results.push_back(dirent);
results.push_back(toDirent(filinfo, path));
}
f_closedir(&dir);
return results;
}
std::map<std::string, std::string> getMetadata(const Path& path)
std::shared_ptr<Dirent> getDirent(const Path& path) override
{
std::map<std::string, std::string> attributes;
mount();
auto pathstr = path.to_str();
auto pathstr = toUpper(path.to_str());
FILINFO filinfo;
FRESULT res = f_stat(pathstr.c_str(), &filinfo);
throwError(res);
attributes[FILENAME] = filinfo.fname;
attributes[LENGTH] = fmt::format("{}", filinfo.fsize);
attributes[FILE_TYPE] = (filinfo.fattrib & AM_DIR) ? "dir" : "file";
attributes[MODE] = modeToString(filinfo.fattrib);
return attributes;
return toDirent(filinfo, path.parent());
}
Bytes getFile(const Path& path)
Bytes getFile(const Path& path) override
{
mount();
auto pathstr = path.to_str();
auto pathstr = toUpper(path.to_str());
FIL fil;
FRESULT res = f_open(&fil, pathstr.c_str(), FA_READ);
throwError(res);
@@ -158,10 +157,10 @@ public:
return bytes;
}
void putFile(const Path& path, const Bytes& bytes)
void putFile(const Path& path, const Bytes& bytes) override
{
mount();
auto pathstr = path.to_str();
auto pathstr = toUpper(path.to_str());
FIL fil;
FRESULT res =
f_open(&fil, pathstr.c_str(), FA_WRITE | FA_CREATE_ALWAYS);
@@ -182,6 +181,52 @@ public:
f_close(&fil);
}
void deleteFile(const Path& path) override
{
mount();
auto pathstr = toUpper(path.to_str());
FRESULT res = f_unlink(pathstr.c_str());
throwError(res);
}
void moveFile(const Path& oldPath, const Path& newPath) override
{
mount();
auto oldPathStr = toUpper(oldPath.to_str());
auto newPathStr = toUpper(newPath.to_str());
FRESULT res = f_rename(oldPathStr.c_str(), newPathStr.c_str());
throwError(res);
}
void createDirectory(const Path& path)
{
mount();
auto pathStr = path.to_str();
FRESULT res = f_mkdir(pathStr.c_str());
throwError(res);
}
private:
std::shared_ptr<Dirent> toDirent(FILINFO& filinfo, const Path& parent)
{
auto dirent = std::make_shared<Dirent>();
dirent->filename = toUpper(filinfo.fname);
dirent->path = parent;
dirent->path.push_back(dirent->filename);
dirent->length = filinfo.fsize;
dirent->file_type =
(filinfo.fattrib & AM_DIR) ? TYPE_DIRECTORY : TYPE_FILE;
dirent->mode = modeToString(filinfo.fattrib);
dirent->attributes[Filesystem::FILENAME] = filinfo.fname;
dirent->attributes[Filesystem::LENGTH] =
fmt::format("{}", filinfo.fsize);
dirent->attributes[Filesystem::FILE_TYPE] =
(filinfo.fattrib & AM_DIR) ? "dir" : "file";
dirent->attributes[Filesystem::MODE] = modeToString(filinfo.fattrib);
return dirent;
}
public:
DRESULT diskRead(BYTE* buffer, LBA_t sector, UINT count)
{

View File

@@ -24,82 +24,92 @@ public:
public:
std::shared_ptr<const Sector> get(
unsigned track, unsigned side, unsigned sectorId)
unsigned track, unsigned side, unsigned sectorId) override
{
auto it = _changedSectors.get(track, side, sectorId);
if (it)
return it;
trackid_t trackid(track, side);
if (_loadedtracks.find(trackid) == _loadedtracks.end())
if (_loadedTracks.find(trackid) == _loadedTracks.end())
populateSectors(track, side);
return _readSectors.get(track, side, sectorId);
return _loadedSectors.get(track, side, sectorId);
}
std::shared_ptr<Sector> put(
unsigned track, unsigned side, unsigned sectorId)
unsigned track, unsigned side, unsigned sectorId) override
{
trackid_t trackid(track, side);
_changedtracks.insert(trackid);
_changedTracks.insert(trackid);
return _changedSectors.put(track, side, sectorId);
}
void flush()
virtual bool isReadOnly()
{
for (const auto& trackid : _changedtracks)
return (_fluxSink == nullptr);
}
bool needsFlushing() override
{
return !_changedTracks.empty();
}
void flushChanges() override
{
std::set<Location> locations;
for (const auto& trackid : _changedTracks)
{
unsigned track = trackid.first;
unsigned side = trackid.second;
auto layoutdata = Layout::getLayoutOfTrack(track, side);
auto sectors = Layout::getSectorsInTrack(layoutdata);
locations.insert(Mapper::computeLocationFor(track, side));
config.mutable_tracks()->Clear();
config.mutable_tracks()->set_start(track);
/* If we don't have all the sectors of this track, we may need to
* populate any non-changed sectors as we can only write a track at
* a time. */
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(
if (!imageContainsAllSectorsOf(
_changedSectors, track, side, sectors))
{
/* Just write directly from the changedsectors image. */
/* If we don't have any loaded sectors for this track, pre-read
* it. */
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())
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;
if (!_changedSectors.contains(track, side, sector))
_changedSectors.put(track, side, sector)->data =
_loadedSectors.get(track, side, sector)->data;
}
writeDiskCommand(
image, *_encoder, *_fluxSink, &*_decoder, &*_fluxSource);
}
}
/* We now have complete tracks which can be written. */
writeDiskCommand(_changedSectors,
*_encoder,
*_fluxSink,
&*_decoder,
&*_fluxSource,
locations);
discardChanges();
}
void discardChanges() override
{
_loadedTracks.clear();
_loadedSectors.clear();
_changedTracks.clear();
_changedSectors.clear();
}
private:
@@ -122,8 +132,8 @@ private:
auto trackdata = readAndDecodeTrack(*_fluxSource, *_decoder, location);
for (const auto& sector : trackdata->sectors)
*_readSectors.put(track, side, sector->logicalSector) = *sector;
_loadedtracks.insert(trackid_t(track, side));
*_loadedSectors.put(track, side, sector->logicalSector) = *sector;
_loadedTracks.insert(trackid_t(track, side));
}
std::shared_ptr<FluxSource> _fluxSource;
@@ -132,10 +142,10 @@ private:
std::shared_ptr<AbstractDecoder> _decoder;
typedef std::pair<unsigned, unsigned> trackid_t;
Image _readSectors;
Image _loadedSectors;
Image _changedSectors;
std::set<trackid_t> _loadedtracks;
std::set<trackid_t> _changedtracks;
std::set<trackid_t> _loadedTracks;
std::set<trackid_t> _changedTracks;
};
std::unique_ptr<SectorInterface> SectorInterface::createFluxSectorInterface(

View File

@@ -1,11 +1,22 @@
#include "lib/globals.h"
#include "lib/vfs/sectorinterface.h"
#include "lib/imagereader/imagereader.h"
#include "lib/imagewriter/imagewriter.h"
#include "lib/image.h"
#include "lib/layout.h"
#include "lib/sector.h"
#include "lib/bytes.h"
class ImageSectorInterface : public SectorInterface
{
public:
ImageSectorInterface(std::shared_ptr<Image> image): _image(image) {}
ImageSectorInterface(std::shared_ptr<ImageReader> reader,
std::shared_ptr<ImageWriter> writer):
_reader(reader),
_writer(writer)
{
discardChanges();
}
public:
std::shared_ptr<const Sector> get(
@@ -17,15 +28,47 @@ public:
std::shared_ptr<Sector> put(
unsigned track, unsigned side, unsigned sectorId)
{
_changed = true;
return _image->put(track, side, sectorId);
}
virtual bool isReadOnly()
{
return (_writer == nullptr);
}
bool needsFlushing() override
{
return _changed;
}
void flushChanges() override
{
_writer->writeImage(*_image);
_changed = false;
}
void discardChanges() override
{
if (_reader)
_image = _reader->readImage();
else
{
_image = std::make_shared<Image>();
_image->createBlankImage();
}
_changed = false;
}
private:
std::shared_ptr<Image> _image;
std::shared_ptr<ImageReader> _reader;
std::shared_ptr<ImageWriter> _writer;
bool _changed = false;
};
std::unique_ptr<SectorInterface> SectorInterface::createImageSectorInterface(
std::shared_ptr<Image> image)
std::shared_ptr<ImageReader> reader, std::shared_ptr<ImageWriter> writer)
{
return std::make_unique<ImageSectorInterface>(image);
return std::make_unique<ImageSectorInterface>(reader, writer);
}

View File

@@ -24,7 +24,13 @@ public:
{
}
std::map<std::string, std::string> getMetadata()
uint32_t capabilities() const
{
return OP_GETFSDATA | OP_CREATE | OP_LIST | OP_GETFILE | OP_PUTFILE |
OP_GETDIRENT | OP_MOVE | OP_CREATEDIR | OP_DELETE;
}
std::map<std::string, std::string> getMetadata() override
{
HfsMount m(this);
@@ -42,21 +48,26 @@ public:
return attributes;
}
FilesystemStatus check()
FilesystemStatus check() override
{
return FS_OK;
}
void create(bool quick, const std::string& volumeName)
void create(bool quick, const std::string& volumeName) override
{
if (!quick)
eraseEverythingOnDisk();
hfs_format(
(const char*)this, 0, HFS_MODE_ANY, volumeName.c_str(), 0, nullptr);
if (hfs_format((const char*)this,
0,
HFS_MODE_ANY,
volumeName.c_str(),
0,
nullptr))
throwError();
}
std::vector<std::shared_ptr<Dirent>> list(const Path& path)
std::vector<std::shared_ptr<Dirent>> list(const Path& path) override
{
HfsMount m(this);
@@ -64,7 +75,7 @@ public:
auto pathstr = ":" + path.to_str(":");
HfsDir dir(hfs_opendir(_vol, pathstr.c_str()));
if (!dir)
throw FileNotFoundException();
throwError();
for (;;)
{
@@ -73,26 +84,12 @@ public:
if (r != 0)
break;
auto dirent = std::make_shared<Dirent>();
dirent->filename = de.name;
if (de.flags & HFS_ISDIR)
{
dirent->file_type = TYPE_DIRECTORY;
}
else
{
dirent->file_type = TYPE_FILE;
dirent->length =
de.u.file.dsize + de.u.file.rsize + AppleSingle::OVERHEAD;
}
dirent->mode = (de.flags & HFS_ISLOCKED) ? "L" : "";
results.push_back(dirent);
results.push_back(toDirent(de, path));
}
return results;
}
std::map<std::string, std::string> getMetadata(const Path& path)
std::shared_ptr<Dirent> getDirent(const Path& path) override
{
HfsMount m(this);
if (path.size() == 0)
@@ -102,46 +99,12 @@ public:
auto pathstr = ":" + path.to_str(":");
hfsdirent de;
if (hfs_stat(_vol, pathstr.c_str(), &de))
throw FileNotFoundException();
throwError();
std::map<std::string, std::string> attributes;
attributes[FILENAME] = de.name;
attributes[LENGTH] = "0";
attributes[FILE_TYPE] = (de.flags & HFS_ISDIR) ? "dir" : "file";
attributes[MODE] = (de.flags & HFS_ISLOCKED) ? "L" : "";
attributes["machfs.ctime"] = toIso8601(de.crdate);
attributes["machfs.mtime"] = toIso8601(de.mddate);
attributes["machfs.last_backup"] = toIso8601(de.bkdate);
attributes["machfs.finder.x"] = fmt::format("{}", de.fdlocation.h);
attributes["machfs.finder.y"] = fmt::format("{}", de.fdlocation.v);
attributes["machfs.finder.flags"] = fmt::format("0x{:x}", de.fdflags);
if (de.flags & HFS_ISDIR)
{
attributes["machfs.dir.valence"] =
fmt::format("{}", de.u.dir.valence);
attributes["machfs.dir.x1"] = fmt::format("{}", de.u.dir.rect.left);
attributes["machfs.dir.y1"] = fmt::format("{}", de.u.dir.rect.top);
attributes["machfs.dir.x2"] =
fmt::format("{}", de.u.dir.rect.right);
attributes["machfs.dir.y2"] =
fmt::format("{}", de.u.dir.rect.bottom);
}
else
{
attributes["length"] = fmt::format("{}",
de.u.file.dsize + de.u.file.rsize + AppleSingle::OVERHEAD);
attributes["machfs.file.dsize"] =
fmt::format("{}", de.u.file.dsize);
attributes["machfs.file.rsize"] =
fmt::format("{}", de.u.file.rsize);
attributes["machfs.file.type"] = de.u.file.type;
attributes["machfs.file.creator"] = de.u.file.creator;
}
return attributes;
return toDirent(de, path.parent());
}
Bytes getFile(const Path& path)
Bytes getFile(const Path& path) override
{
HfsMount m(this);
if (path.size() == 0)
@@ -151,7 +114,7 @@ public:
auto pathstr = ":" + path.to_str(":");
HfsFile file(hfs_open(_vol, pathstr.c_str()));
if (!file)
throw FileNotFoundException();
throwError();
AppleSingle a;
@@ -168,7 +131,7 @@ public:
return a.render();
}
void putFile(const Path& path, const Bytes& bytes)
void putFile(const Path& path, const Bytes& bytes) override
{
HfsMount m(this);
if (path.size() == 0)
@@ -192,7 +155,7 @@ public:
(const char*)a.type.cbegin(),
(const char*)a.creator.cbegin()));
if (!file)
throw CannotWriteException();
throwError();
hfs_setfork(file, 0);
writeBytes(file, a.data);
@@ -200,6 +163,146 @@ public:
writeBytes(file, a.rsrc);
}
void deleteFile(const Path& path) override
{
HfsMount m(this);
if (path.size() == 0)
throw BadPathException();
auto pathstr = ":" + path.to_str(":");
if (hfs_delete(_vol, pathstr.c_str()))
throwError();
}
void moveFile(const Path& oldPath, const Path& newPath) override
{
HfsMount m(this);
if (oldPath.empty() || newPath.empty())
throw BadPathException();
auto oldPathStr = ":" + oldPath.to_str(":");
auto newPathStr = ":" + newPath.to_str(":");
if (hfs_rename(_vol, oldPathStr.c_str(), newPathStr.c_str()))
throwError();
}
void createDirectory(const Path& path) override
{
HfsMount m(this);
auto pathStr = ":" + path.to_str(":");
if (hfs_mkdir(_vol, pathStr.c_str()))
throwError();
}
private:
std::shared_ptr<Dirent> toDirent(hfsdirent& de, const Path& parent)
{
auto dirent = std::make_shared<Dirent>();
dirent->filename = de.name;
dirent->path = parent;
dirent->path.push_back(de.name);
if (de.flags & HFS_ISDIR)
dirent->file_type = TYPE_DIRECTORY;
else
{
dirent->file_type = TYPE_FILE;
dirent->length =
de.u.file.dsize + de.u.file.rsize + AppleSingle::OVERHEAD;
}
dirent->mode = (de.flags & HFS_ISLOCKED) ? "L" : "";
dirent->attributes[FILENAME] = de.name;
dirent->attributes[LENGTH] = "0";
dirent->attributes[FILE_TYPE] = (de.flags & HFS_ISDIR) ? "dir" : "file";
dirent->attributes[MODE] = (de.flags & HFS_ISLOCKED) ? "L" : "";
dirent->attributes["machfs.ctime"] = toIso8601(de.crdate);
dirent->attributes["machfs.mtime"] = toIso8601(de.mddate);
dirent->attributes["machfs.last_backup"] = toIso8601(de.bkdate);
dirent->attributes["machfs.finder.x"] =
fmt::format("{}", de.fdlocation.h);
dirent->attributes["machfs.finder.y"] =
fmt::format("{}", de.fdlocation.v);
dirent->attributes["machfs.finder.flags"] =
fmt::format("0x{:x}", de.fdflags);
if (de.flags & HFS_ISDIR)
{
dirent->attributes["machfs.dir.valence"] =
fmt::format("{}", de.u.dir.valence);
dirent->attributes["machfs.dir.x1"] =
fmt::format("{}", de.u.dir.rect.left);
dirent->attributes["machfs.dir.y1"] =
fmt::format("{}", de.u.dir.rect.top);
dirent->attributes["machfs.dir.x2"] =
fmt::format("{}", de.u.dir.rect.right);
dirent->attributes["machfs.dir.y2"] =
fmt::format("{}", de.u.dir.rect.bottom);
}
else
{
dirent->attributes["length"] = fmt::format("{}",
de.u.file.dsize + de.u.file.rsize + AppleSingle::OVERHEAD);
dirent->attributes["machfs.file.dsize"] =
fmt::format("{}", de.u.file.dsize);
dirent->attributes["machfs.file.rsize"] =
fmt::format("{}", de.u.file.rsize);
dirent->attributes["machfs.file.type"] = de.u.file.type;
dirent->attributes["machfs.file.creator"] = de.u.file.creator;
}
return dirent;
}
void throwError()
{
auto message =
fmt::format("HFS error: {}", hfs_error ? hfs_error : "unknown");
switch (errno)
{
case ENOTDIR:
case ENAMETOOLONG:
if (hfs_error)
throw BadPathException(message);
else
throw BadPathException();
case ENOENT:
if (hfs_error)
throw FileNotFoundException(message);
else
throw FileNotFoundException();
case EEXIST:
throw BadPathException("That already exists");
case ENOTEMPTY:
throw BadPathException("Directory is not empty");
case EISDIR:
throw BadPathException("That's a directory");
case EIO:
case EINVAL:
if (hfs_error)
throw BadFilesystemException(message);
else
throw BadFilesystemException();
case ENOSPC:
case ENOMEM:
if (hfs_error)
throw DiskFullException(message);
else
throw DiskFullException();
case EROFS:
if (hfs_error)
throw ReadOnlyFilesystemException(message);
else
throw ReadOnlyFilesystemException();
}
}
private:
Bytes readBytes(hfsfile* file)
{

View File

@@ -1,7 +1,8 @@
#ifndef SECTORINTERFACE_H
#define SECTORINTERFACE_H
class Image;
class ImageReader;
class ImageWriter;
class Sector;
class FluxSource;
class FluxSink;
@@ -16,11 +17,24 @@ public:
virtual std::shared_ptr<Sector> put(
unsigned track, unsigned side, unsigned sectorId) = 0;
virtual void flush() {}
virtual bool isReadOnly()
{
return true;
}
virtual bool needsFlushing()
{
return false;
}
virtual void flushChanges() {}
virtual void discardChanges() {}
public:
static std::unique_ptr<SectorInterface> createImageSectorInterface(
std::shared_ptr<Image> image);
std::shared_ptr<ImageReader> reader,
std::shared_ptr<ImageWriter> writer);
static std::unique_ptr<SectorInterface> createFluxSectorInterface(
std::shared_ptr<FluxSource> fluxSource,
std::shared_ptr<FluxSink> fluxSink,

View File

@@ -6,9 +6,20 @@
#include "lib/image.h"
#include "lib/sector.h"
#include "lib/vfs/sectorinterface.h"
#include "lib/imagereader/imagereader.h"
#include "lib/imagewriter/imagewriter.h"
#include "lib/fluxsource/fluxsource.h"
#include "lib/fluxsink/fluxsink.h"
#include "lib/decoders/decoders.h"
#include "lib/encoders/encoders.h"
#include "lib/config.pb.h"
#include "lib/utils.h"
Path::Path(const std::vector<std::string> other):
std::vector<std::string>(other)
{
}
Path::Path(const std::string& path)
{
if (path == "")
@@ -28,11 +39,34 @@ Path::Path(const std::string& path)
}
}
Path Path::parent() const
{
Path p;
if (!empty())
{
for (int i = 0; i < (size() - 1); i++)
p.push_back((*this)[i]);
}
return p;
}
Path Path::concat(const std::string& s) const
{
Path p(*this);
p.push_back(s);
return p;
}
std::string Path::to_str(const std::string sep) const
{
return join(*this, sep);
}
uint32_t Filesystem::capabilities() const
{
return 0;
}
void Filesystem::create(bool quick, const std::string& volumeName)
{
throw UnimplementedFilesystemException();
@@ -68,7 +102,7 @@ void Filesystem::putFile(const Path& path, const Bytes& data)
throw UnimplementedFilesystemException();
}
std::map<std::string, std::string> Filesystem::getMetadata(const Path& path)
std::shared_ptr<Dirent> Filesystem::getDirent(const Path& path)
{
throw UnimplementedFilesystemException();
}
@@ -89,9 +123,29 @@ void Filesystem::deleteFile(const Path& path)
throw UnimplementedFilesystemException();
}
void Filesystem::flush()
void Filesystem::moveFile(const Path& oldName, const Path& newName)
{
_sectors->flush();
throw UnimplementedFilesystemException();
}
bool Filesystem::isReadOnly()
{
return _sectors->isReadOnly();
}
bool Filesystem::needsFlushing()
{
return _sectors->needsFlushing();
}
void Filesystem::flushChanges()
{
_sectors->flushChanges();
}
void Filesystem::discardChanges()
{
_sectors->discardChanges();
}
Filesystem::Filesystem(std::shared_ptr<SectorInterface> sectors):
@@ -153,6 +207,47 @@ std::unique_ptr<Filesystem> Filesystem::createFilesystem(
}
}
std::unique_ptr<Filesystem> Filesystem::createFilesystemFromConfig()
{
std::shared_ptr<SectorInterface> sectorInterface;
if (config.has_flux_source() || config.has_flux_sink())
{
std::shared_ptr<FluxSource> fluxSource;
std::shared_ptr<AbstractDecoder> decoder;
std::shared_ptr<FluxSink> fluxSink;
std::shared_ptr<AbstractEncoder> encoder;
if (config.flux_source().source_case() !=
FluxSourceProto::SOURCE_NOT_SET)
{
fluxSource = FluxSource::create(config.flux_source());
decoder = AbstractDecoder::create(config.decoder());
}
if (config.flux_sink().has_drive())
{
fluxSink = FluxSink::create(config.flux_sink());
encoder = AbstractEncoder::create(config.encoder());
}
sectorInterface = SectorInterface::createFluxSectorInterface(
fluxSource, fluxSink, encoder, decoder);
}
else
{
std::shared_ptr<ImageReader> reader;
std::shared_ptr<ImageWriter> writer;
if (config.image_reader().format_case() !=
ImageReaderProto::FORMAT_NOT_SET)
reader = ImageReader::create(config.image_reader());
if (config.image_writer().format_case() !=
ImageWriterProto::FORMAT_NOT_SET)
writer = ImageWriter::create(config.image_writer());
sectorInterface =
SectorInterface::createImageSectorInterface(reader, writer);
}
return createFilesystem(config.filesystem(), sectorInterface);
}
Bytes Filesystem::getSector(unsigned track, unsigned side, unsigned sector)
{
auto s = _sectors->get(track, side, sector);

View File

@@ -10,18 +10,34 @@ class DfsProto;
class FilesystemProto;
class SectorInterface;
class Path : public std::vector<std::string>
{
public:
Path() {}
Path(const std::vector<std::string> other);
Path(const std::string& text);
public:
Path parent() const;
Path concat(const std::string& s) const;
std::string to_str(const std::string sep = "/") const;
};
enum FileType
{
TYPE__INVALID,
TYPE_FILE,
TYPE_DIRECTORY
};
struct Dirent
{
Path path;
std::string filename;
FileType file_type;
uint32_t length;
std::string mode;
std::map<std::string, std::string> attributes;
};
enum FilesystemStatus
@@ -33,37 +49,50 @@ enum FilesystemStatus
FS_BAD
};
class FilesystemException
class FilesystemException : public ErrorException
{
public:
FilesystemException(const std::string& message): message(message) {}
public:
std::string message;
FilesystemException(const std::string& message): ErrorException(message) {}
};
class BadPathException : public FilesystemException
{
public:
BadPathException(): FilesystemException("Bad path") {}
BadPathException(const std::string& msg): FilesystemException(msg) {}
};
class FileNotFoundException : public FilesystemException
{
public:
FileNotFoundException(): FilesystemException("File not found") {}
FileNotFoundException(const std::string& msg): FilesystemException(msg) {}
};
class BadFilesystemException : public FilesystemException
{
public:
BadFilesystemException(): FilesystemException("Invalid filesystem") {}
BadFilesystemException(const std::string& msg): FilesystemException(msg) {}
};
class CannotWriteException : public FilesystemException
{
public:
CannotWriteException(): FilesystemException("Cannot write file") {}
CannotWriteException(const std::string& msg): FilesystemException(msg) {}
};
class DiskFullException : public CannotWriteException
{
public:
DiskFullException(): CannotWriteException("Disk is full") {}
DiskFullException(const std::string& msg): CannotWriteException(msg) {}
};
class ReadOnlyFilesystemException : public FilesystemException
@@ -72,6 +101,11 @@ public:
ReadOnlyFilesystemException(): FilesystemException("Read only filesystem")
{
}
ReadOnlyFilesystemException(const std::string& msg):
FilesystemException(msg)
{
}
};
class UnimplementedFilesystemException : public FilesystemException
@@ -88,16 +122,6 @@ public:
}
};
class Path : public std::vector<std::string>
{
public:
Path() {}
Path(const std::string& text);
public:
std::string to_str(const std::string sep = "/") const;
};
class Filesystem
{
public:
@@ -111,9 +135,28 @@ public:
static constexpr const char* USED_BLOCKS = "used_blocks";
static constexpr const char* BLOCK_SIZE = "block_size";
enum
{
OP_CREATE = 0b0000000000000001,
OP_CHECK = 0b0000000000000010,
OP_LIST = 0b0000000000000100,
OP_GETFILE = 0b0000000000001000,
OP_PUTFILE = 0b0000000000010000,
OP_GETDIRENT = 0b0000000000100000,
OP_CREATEDIR = 0b0000000001000000,
OP_DELETE = 0b0000000010000000,
OP_GETFSDATA = 0b0000000100000000,
OP_PUTFSDATA = 0b0000001000000000,
OP_PUTATTRS = 0b0000010000000000,
OP_MOVE = 0b0000100000000000,
};
public:
/* Retrieve capability information. */
virtual uint32_t capabilities() const;
/* Create a filesystem on the disk. */
virtual void create(bool quick, const std::string& volmeName);
virtual void create(bool quick, const std::string& volumeName);
/* Are all sectors on the filesystem present and good? (Does not check
* filesystem consistency.) */
@@ -135,8 +178,8 @@ public:
/* Write a file. */
virtual void putFile(const Path& path, const Bytes& data);
/* Get file metadata. */
virtual std::map<std::string, std::string> getMetadata(const Path& path);
/* Get a single file dirent. */
virtual std::shared_ptr<Dirent> getDirent(const Path& path);
/* Update file metadata. */
virtual void putMetadata(
@@ -148,8 +191,20 @@ public:
/* Deletes a file or non-empty directory. */
virtual void deleteFile(const Path& path);
/* Moves a file (including renaming it). */
virtual void moveFile(const Path& oldName, const Path& newName);
/* Is this filesystem's backing store read-only? */
bool isReadOnly();
/* Does this filesystem need flushing? */
bool needsFlushing();
/* Flushes any changes back to the disk. */
void flush();
void flushChanges();
/* Discards any pending changes. */
void discardChanges();
public:
Filesystem(std::shared_ptr<SectorInterface> sectors);
@@ -188,6 +243,7 @@ public:
static std::unique_ptr<Filesystem> createFilesystem(
const FilesystemProto& config, std::shared_ptr<SectorInterface> image);
static std::unique_ptr<Filesystem> createFilesystemFromConfig();
};
#endif

View File

@@ -9,6 +9,9 @@ FLUXENGINE_SRCS = \
src/fe-getfileinfo.cc \
src/fe-inspect.cc \
src/fe-ls.cc \
src/fe-mkdir.cc \
src/fe-mv.cc \
src/fe-rm.cc \
src/fe-putfile.cc \
src/fe-rawread.cc \
src/fe-rawwrite.cc \

View File

@@ -30,9 +30,9 @@ int mainFormat(int argc, const char* argv[])
try
{
auto filesystem = createFilesystemFromConfig();
auto filesystem = Filesystem::createFilesystemFromConfig();
filesystem->create(quick, volumeName);
filesystem->flush();
filesystem->flushChanges();
}
catch (const FilesystemException& e)
{

View File

@@ -27,7 +27,7 @@ int mainGetDiskInfo(int argc, const char* argv[])
try
{
auto filesystem = createFilesystemFromConfig();
auto filesystem = Filesystem::createFilesystemFromConfig();
auto attributes = filesystem->getMetadata();
for (const auto& e : attributes)

View File

@@ -37,7 +37,7 @@ int mainGetFile(int argc, const char* argv[])
if (outputFilename.empty())
outputFilename = inputFilename.back();
auto filesystem = createFilesystemFromConfig();
auto filesystem = Filesystem::createFilesystemFromConfig();
auto data = filesystem->getFile(inputFilename);
data.writeToFile(outputFilename);
}

View File

@@ -29,10 +29,10 @@ int mainGetFileInfo(int argc, const char* argv[])
try
{
auto filesystem = createFilesystemFromConfig();
auto attributes = filesystem->getMetadata(Path(directory));
auto filesystem = Filesystem::createFilesystemFromConfig();
auto dirent = filesystem->getDirent(Path(directory));
for (const auto& e : attributes)
for (const auto& e : dirent->attributes)
fmt::print("{}={}\n", e.first, quote(e.second));
}
catch (const FilesystemException& e)

View File

@@ -43,7 +43,7 @@ int mainLs(int argc, const char* argv[])
try
{
auto filesystem = createFilesystemFromConfig();
auto filesystem = Filesystem::createFilesystemFromConfig();
auto files = filesystem->list(Path(directory));
int maxlen = 0;

39
src/fe-mkdir.cc Normal file
View File

@@ -0,0 +1,39 @@
#include "globals.h"
#include "flags.h"
#include "proto.h"
#include "fmt/format.h"
#include "fluxengine.h"
#include "lib/vfs/vfs.h"
#include "lib/utils.h"
#include "src/fileutils.h"
#include <google/protobuf/text_format.h>
#include <fstream>
static FlagGroup flags({&fileFlags});
static StringFlag filename({"-p", "--path"}, "directory to create", "");
int mainMkDir(int argc, const char* argv[])
{
if (argc == 1)
showProfiles("mkdir", formats);
flags.parseFlagsWithConfigFiles(argc, argv, formats);
try
{
auto filesystem = Filesystem::createFilesystemFromConfig();
Path path(filename);
if (path.size() == 0)
Error() << "filename missing";
filesystem->createDirectory(path);
filesystem->flushChanges();
}
catch (const FilesystemException& e)
{
Error() << e.message;
}
return 0;
}

44
src/fe-mv.cc Normal file
View File

@@ -0,0 +1,44 @@
#include "globals.h"
#include "flags.h"
#include "proto.h"
#include "fmt/format.h"
#include "fluxengine.h"
#include "lib/vfs/vfs.h"
#include "lib/utils.h"
#include "src/fileutils.h"
#include <google/protobuf/text_format.h>
#include <fstream>
static FlagGroup flags({&fileFlags});
static StringFlag oldFilename({"--path1"}, "old filename", "");
static StringFlag newFilename({"--path2"}, "new filename", "");
int mainMv(int argc, const char* argv[])
{
if (argc == 1)
showProfiles("mv", formats);
flags.parseFlagsWithConfigFiles(argc, argv, formats);
try
{
auto filesystem = Filesystem::createFilesystemFromConfig();
Path oldPath(oldFilename);
if (oldPath.size() == 0)
Error() << "old filename missing";
Path newPath(newFilename);
if (newPath.size() == 0)
Error() << "new filename missing";
filesystem->moveFile(oldPath, newPath);
filesystem->flushChanges();
}
catch (const FilesystemException& e)
{
Error() << e.message;
}
return 0;
}

View File

@@ -38,9 +38,9 @@ int mainPutFile(int argc, const char* argv[])
Error() << "you must supply a destination path to write to";
auto data = Bytes::readFromFile(inputFilename);
auto filesystem = createFilesystemFromConfig();
auto filesystem = Filesystem::createFilesystemFromConfig();
filesystem->putFile(outputFilename, data);
filesystem->flush();
filesystem->flushChanges();
}
catch (const FilesystemException& e)
{

39
src/fe-rm.cc Normal file
View File

@@ -0,0 +1,39 @@
#include "globals.h"
#include "flags.h"
#include "proto.h"
#include "fmt/format.h"
#include "fluxengine.h"
#include "lib/vfs/vfs.h"
#include "lib/utils.h"
#include "src/fileutils.h"
#include <google/protobuf/text_format.h>
#include <fstream>
static FlagGroup flags({&fileFlags});
static StringFlag filename({"-p", "--path"}, "filename to remove", "");
int mainRm(int argc, const char* argv[])
{
if (argc == 1)
showProfiles("rm", formats);
flags.parseFlagsWithConfigFiles(argc, argv, formats);
try
{
auto filesystem = Filesystem::createFilesystemFromConfig();
Path path(filename);
if (path.size() == 0)
Error() << "filename missing";
filesystem->deleteFile(path);
filesystem->flushChanges();
}
catch (const FilesystemException& e)
{
Error() << e.message;
}
return 0;
}

View File

@@ -4,11 +4,10 @@
#include "sector.h"
#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 "lib/imagewriter/imagewriter.h"
#include "fmt/format.h"
#include "fluxengine.h"
#include "lib/vfs/sectorinterface.h"
@@ -25,6 +24,8 @@ static StringFlag image({"-i", "--image"},
{
ImageReader::updateConfigForFilename(
config.mutable_image_reader(), value);
ImageWriter::updateConfigForFilename(
config.mutable_image_writer(), value);
});
static StringFlag flux({"-f", "--flux"},
@@ -38,24 +39,3 @@ static StringFlag flux({"-f", "--flux"},
config.mutable_flux_sink(), value);
});
std::unique_ptr<Filesystem> createFilesystemFromConfig()
{
std::shared_ptr<SectorInterface> sectorInterface;
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, fluxSink, encoder, decoder);
}
else
{
auto reader = ImageReader::create(config.image_reader());
std::shared_ptr<Image> image(std::move(reader->readImage()));
sectorInterface = SectorInterface::createImageSectorInterface(image);
}
return Filesystem::createFilesystem(config.filesystem(), sectorInterface);
}

View File

@@ -3,7 +3,5 @@
extern FlagGroup fileFlags;
extern std::unique_ptr<Filesystem> createFilesystemFromConfig();
#endif

View File

@@ -12,10 +12,13 @@ extern command_cb mainGetFile;
extern command_cb mainGetFileInfo;
extern command_cb mainInspect;
extern command_cb mainLs;
extern command_cb mainMkDir;
extern command_cb mainMv;
extern command_cb mainPutFile;
extern command_cb mainRawRead;
extern command_cb mainRawWrite;
extern command_cb mainRead;
extern command_cb mainRm;
extern command_cb mainRpm;
extern command_cb mainSeek;
extern command_cb mainTestBandwidth;
@@ -43,9 +46,12 @@ static std::vector<Command> commands =
{ "rawwrite", mainRawWrite, "Writes a flux file to a disk. Warning: you can't use this to copy disks.", },
{ "getdiskinfo", mainGetDiskInfo, "Read volume metadata off a disk (or image).", },
{ "ls", mainLs, "Show files on disk (or image).", },
{ "mv", mainMv, "Rename a file on a disk (or image).", },
{ "rm", mainRm, "Deletes a file (or directory) 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).", },
{ "putfile", mainPutFile, "Write a file to disk (or image).", },
{ "mkdir", mainMkDir, "Create a directory on disk (or image).", },
{ "rpm", mainRpm, "Measures the disk rotational speed.", },
{ "seek", mainSeek, "Moves the disk head.", },
{ "test", mainTest, "Various testing commands.", },
@@ -133,7 +139,7 @@ void showProfiles(const std::string& command, const std::map<std::string, std::s
ConfigProto config;
if (!config.ParseFromString(it.second))
Error() << "couldn't load config proto";
if (config.is_extension())
if (config.is_extension() && (it.first[0] != '_'))
std::cout << fmt::format(" {}: {}\n", it.first, config.comment());
}

View File

@@ -0,0 +1,3 @@
comment: 'Common Atari definitions'
is_extension: true

View File

@@ -1,5 +1,7 @@
comment: 'Atari ST 360kB 3.5" 80-track 9-sector SSDD'
include: '_atari'
image_reader {
filename: "atarist360.st"
img {}
@@ -53,7 +55,3 @@ heads {
end: 0
}
filesystem {
fatfs {}
}

View File

@@ -1,5 +1,7 @@
comment: 'Atari ST 370kB 3.5" 82-track 9-sector SSDD'
include: '_atari'
image_reader {
filename: "atarist370.st"
img {}
@@ -53,8 +55,4 @@ heads {
end: 0
}
filesystem {
fatfs {}
}

View File

@@ -1,5 +1,7 @@
comment: 'Atari ST 400kB 3.5" 80-track 10-sector SSDD'
include: '_atari'
image_reader {
filename: "atarist400.st"
img {}
@@ -53,8 +55,4 @@ heads {
end: 0
}
filesystem {
fatfs {}
}

View File

@@ -1,5 +1,7 @@
comment: 'Atari ST 410kB 3.5" 82-track 10-sector SSDD'
include: '_atari'
image_reader {
filename: "atarist410.st"
img {}
@@ -53,8 +55,4 @@ heads {
end: 0
}
filesystem {
fatfs {}
}

View File

@@ -1,5 +1,7 @@
comment: 'Atari ST 720kB 3.5" 80-track 9-sector DSDD'
include: '_atari'
image_reader {
filename: "atarist720.st"
img {}
@@ -53,8 +55,4 @@ heads {
end: 1
}
filesystem {
fatfs {}
}

View File

@@ -1,5 +1,7 @@
comment: 'Atari ST 740kB 3.5" 82-track 9-sector DSDD'
include: '_atari'
image_reader {
filename: "atarist740.st"
img {}
@@ -53,8 +55,4 @@ heads {
end: 1
}
filesystem {
fatfs {}
}

View File

@@ -1,5 +1,7 @@
comment: 'Atari ST 800kB 3.5" 80-track 10-sector DSDD'
include: '_atari'
image_reader {
filename: "atarist800.st"
img {}
@@ -53,8 +55,4 @@ heads {
end: 1
}
filesystem {
fatfs {}
}

View File

@@ -1,5 +1,7 @@
comment: 'Atari ST 820kB 3.5" 82-track 10-sector DSDD'
include: '_atari'
image_reader {
filename: "atarist820.st"
img {}
@@ -53,8 +55,4 @@ heads {
end: 1
}
filesystem {
fatfs {}
}

View File

@@ -1,4 +1,5 @@
FORMATS = \
_atari \
_micropolis \
_northstar \
_mx \
@@ -72,7 +73,7 @@ $(OBJDIR)/src/formats/format_%.cc: $(OBJDIR)/protoencode_ConfigProto.exe src/for
OBJS += $(patsubst %, $(OBJDIR)/src/formats/format_%.o, $(FORMATS))
$(OBJDIR)/src/formats/table.cc: scripts/mktable.sh Makefile
$(OBJDIR)/src/formats/table.cc: scripts/mktable.sh src/formats/build.mk
@mkdir -p $(dir $@)
@echo MKTABLE $@
@scripts/mktable.sh formats $(FORMATS) > $@

View File

@@ -2,6 +2,7 @@ ifneq ($(shell $(WX_CONFIG) --version),)
FLUXENGINE_GUI_SRCS = \
src/gui/customstatusbar.cc \
src/gui/filesystemmodel.cc \
src/gui/fluxviewercontrol.cc \
src/gui/fluxviewerwindow.cc \
src/gui/layout.cpp \
@@ -9,6 +10,7 @@ FLUXENGINE_GUI_SRCS = \
src/gui/mainwindow.cc \
src/gui/texteditorwindow.cc \
src/gui/textviewerwindow.cc \
src/gui/fileviewerwindow.cc \
src/gui/visualisationcontrol.cc \
FLUXENGINE_GUI_OBJS = \
@@ -17,9 +19,9 @@ FLUXENGINE_GUI_OBJS = \
)
OBJS += $(FLUXENGINE_GUI_OBJS)
$(FLUXENGINE_GUI_SRCS): | $(PROTO_HDRS)
$(FLUXENGINE_GUI_OBJS): CFLAGS += $(shell $(WX_CONFIG) --cxxflags core base adv)
$(FLUXENGINE_GUI_OBJS): CFLAGS += $(shell $(WX_CONFIG) --cxxflags core base adv aui)
FLUXENGINE_GUI_BIN = $(OBJDIR)/fluxengine-gui.exe
$(FLUXENGINE_GUI_BIN): LDFLAGS += $(shell $(WX_CONFIG) --libs core base adv)
$(FLUXENGINE_GUI_BIN): LDFLAGS += $(shell $(WX_CONFIG) --libs core base adv aui)
$(FLUXENGINE_GUI_BIN): $(FLUXENGINE_GUI_OBJS)
$(call use-pkgconfig, $(FLUXENGINE_GUI_BIN), $(FLUXENGINE_GUI_OBJS), fmt)
@@ -28,6 +30,9 @@ $(call use-library, $(FLUXENGINE_GUI_BIN), $(FLUXENGINE_GUI_OBJS), LIBFLUXENGINE
$(call use-library, $(FLUXENGINE_GUI_BIN), $(FLUXENGINE_GUI_OBJS), LIBFORMATS)
$(call use-library, $(FLUXENGINE_GUI_BIN), $(FLUXENGINE_GUI_OBJS), LIBUSBP)
$(call use-library, $(FLUXENGINE_GUI_BIN), $(FLUXENGINE_GUI_OBJS), PROTO)
$(call use-library, $(FLUXENGINE_GUI_BIN), $(FLUXENGINE_GUI_OBJS), FATFS)
$(call use-library, $(FLUXENGINE_GUI_BIN), $(FLUXENGINE_GUI_OBJS), ADFLIB)
$(call use-library, $(FLUXENGINE_GUI_BIN), $(FLUXENGINE_GUI_OBJS), HFSUTILS)
binaries: fluxengine-gui$(EXT)

299
src/gui/filesystemmodel.cc Normal file
View File

@@ -0,0 +1,299 @@
#include "lib/globals.h"
#include "gui.h"
#include "filesystemmodel.h"
#include "lib/vfs/vfs.h"
#include <wx/artprov.h>
#include <fmt/format.h>
static uintptr_t nodeCount = 0;
FilesystemNode::FilesystemNode(std::shared_ptr<Dirent> dirent):
dirent(dirent),
item((void*)nodeCount++)
{
}
class FilesystemModelImpl : public FilesystemModel
{
public:
FilesystemModelImpl():
_fileIcon(wxArtProvider::GetIcon(wxART_NORMAL_FILE, wxART_BUTTON)),
_folderOpenIcon(
wxArtProvider::GetIcon(wxART_FOLDER_OPEN, wxART_BUTTON)),
_folderClosedIcon(wxArtProvider::GetIcon(wxART_FOLDER, wxART_BUTTON))
{
_root = std::make_shared<FilesystemNode>(std::make_shared<Dirent>());
_root->dirent->file_type = TYPE_DIRECTORY;
_byItem[_root->item] = _root;
ItemAdded(wxDataViewItem(), _root->item);
}
/* --- DataViewModel API --------------------------------------------- */
unsigned int GetColumnCount() const override
{
return 3;
}
wxString GetColumnType(unsigned int column) const override
{
switch (column)
{
case 0:
return "wxDataViewIconText";
case 1:
case 2:
return "string";
default:
wxFAIL;
return "<bad>";
}
}
bool IsContainer(const wxDataViewItem& item) const override
{
auto node = Find(item);
if (!node)
return false;
return node->dirent->file_type == TYPE_DIRECTORY;
}
wxDataViewItem GetParent(const wxDataViewItem& item) const override
{
auto node = Find(item);
if (!node || (node == _root))
return wxDataViewItem();
return Find(node->dirent->path.parent())->item;
}
void GetValue(wxVariant& value,
const wxDataViewItem& item,
unsigned column) const override
{
auto node = Find(item);
if (!node)
return;
if (node->stub)
{
switch (column)
{
case 0:
value << wxDataViewIconText("...loading...");
break;
case 1:
value = "";
break;
case 2:
value = "";
break;
default:
wxFAIL;
}
}
else
{
switch (column)
{
case 0:
value << wxDataViewIconText(node->dirent->filename,
(node->dirent->file_type == TYPE_DIRECTORY)
? _folderClosedIcon
: _fileIcon);
break;
case 1:
value = std::to_string(node->dirent->length);
break;
case 2:
value = node->dirent->mode;
break;
default:
wxFAIL;
}
}
}
bool SetValue(const wxVariant& value,
const wxDataViewItem& item,
unsigned column) override
{
auto node = Find(item);
if (!node || node->stub)
return false;
if ((column == 0) && (value.GetType() == "wxDataViewIconText"))
{
wxDataViewIconText dvit;
dvit << value;
node->newname = dvit.GetText();
return true;
}
return false;
}
unsigned GetChildren(const wxDataViewItem& item,
wxDataViewItemArray& children) const override
{
auto node = Find(item);
if (!node)
return 0;
for (auto& e : node->children)
children.Add(e.second->item);
return node->children.size();
}
/* --- Mutation API -------------------------------------------------- */
void Clear(const Path& path) override
{
auto top = Find(path);
if (!top)
return;
for (;;)
{
auto it = top->children.begin();
if (it == top->children.end())
break;
auto child = it->second;
if (!child->stub)
{
Clear(child->dirent->path);
_byItem.erase(child->item);
}
top->children.erase(it);
ItemDeleted(top->item, child->item);
}
}
std::shared_ptr<FilesystemNode> Find(const Path& path) const override
{
if (path.empty())
return _root;
auto node = _root;
for (const auto& element : path)
{
try
{
node = node->children.at(element);
}
catch (std::out_of_range& e)
{
return nullptr;
}
}
return node;
}
std::shared_ptr<FilesystemNode> Find(
const wxDataViewItem& item) const override
{
if (!item.IsOk())
return _root;
auto it = _byItem.find(item);
if (it == _byItem.end())
return nullptr;
auto node = it->second.lock();
if (node)
return node;
/* This node is stale; clean it out of the weak reference map. */
_byItem.erase(item);
return nullptr;
}
void Delete(const Path& path) override
{
auto parent = Find(path.parent());
if (!parent)
return;
auto child = Find(path);
if (!child)
return;
Clear(path);
_byItem.erase(child->item);
parent->children.erase(child->dirent->filename);
ItemDeleted(parent->item, child->item);
}
void RemoveStub(const Path& path) override
{
auto node = Find(path);
if (!node)
return;
/* If the only item in the directory is a stub, remove it. */
if ((node->children.size() == 1) &&
node->children.begin()->second->stub)
{
}
}
void Add(std::shared_ptr<Dirent> dirent) override
{
auto parent = Find(dirent->path.parent());
if (!parent)
return;
/* Add the actual item (the easy bit). */
auto node = std::make_shared<FilesystemNode>(dirent);
_byItem[node->item] = node;
parent->children[dirent->filename] = node;
ItemAdded(parent->item, node->item);
/* If this is a new directory, add the stub item to it. */
if (dirent->file_type == TYPE_DIRECTORY)
{
auto stub =
std::make_shared<FilesystemNode>(std::make_shared<Dirent>());
_byItem[stub->item] = stub;
node->children[""] = stub;
stub->stub = true;
stub->dirent->path = dirent->path;
stub->dirent->path.push_back("");
ItemAdded(node->item, stub->item);
}
}
private:
std::shared_ptr<FilesystemNode> _root;
mutable std::map<wxDataViewItem, std::weak_ptr<FilesystemNode>> _byItem;
wxIcon _fileIcon;
wxIcon _folderOpenIcon;
wxIcon _folderClosedIcon;
};
FilesystemModel* FilesystemModel::Associate(wxDataViewCtrl* control)
{
auto model = new FilesystemModelImpl();
control->AssociateModel(nullptr);
control->AssociateModel(model);
return model;
}
// vim: sw=4 ts=4 et

38
src/gui/filesystemmodel.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef FILESYSTEMMODEL_H
#define FILESYSTEMMODEL_H
#include <wx/dataview.h>
class Dirent;
class Path;
class FilesystemNode
{
public:
FilesystemNode(std::shared_ptr<Dirent> dirent);
wxDataViewItem item;
bool populated = false;
bool populating = false;
bool stub = false;
std::shared_ptr<Dirent> dirent;
std::map<std::string, std::shared_ptr<FilesystemNode>> children;
std::string newname; /* used for inline renames */
};
class FilesystemModel : public wxDataViewModel
{
public:
virtual void Clear(const Path& path) = 0;
virtual std::shared_ptr<FilesystemNode> Find(const Path& path) const = 0;
virtual std::shared_ptr<FilesystemNode> Find(
const wxDataViewItem& item) const = 0;
virtual void Delete(const Path& path) = 0;
virtual void RemoveStub(const Path& path) = 0;
virtual void Add(std::shared_ptr<Dirent> dirent) = 0;
public:
static FilesystemModel* Associate(wxDataViewCtrl* control);
};
#endif

View File

@@ -0,0 +1,50 @@
#include "lib/globals.h"
#include "lib/utils.h"
#include "lib/bytes.h"
#include "gui.h"
#include "layout.h"
#include "fileviewerwindow.h"
#include "fmt/format.h"
FileViewerWindow::FileViewerWindow(
wxWindow* parent, const std::string& title, const Bytes& data):
FileViewerWindowGen(parent)
{
auto size = hexControl->GetTextExtent("M");
SetSize(size.Scale(85, 25));
SetTitle(title);
{
std::stringstream ss;
hexdump(ss, data);
hexControl->SetValue(ss.str());
}
{
std::stringstream ss;
bool nl = false;
for (uint8_t c : data)
{
if ((c == '\r') && nl)
{
nl = false;
continue;
}
if ((c == '\n') || ((c >= 32) && (c <= 126)))
ss << (char)c;
else if (c == '\r')
ss << '\n';
else
ss << fmt::format("\\x{:02x}", c);
nl = (c == '\n');
}
textControl->SetValue(ss.str());
}
}
void FileViewerWindow::OnClose(wxCloseEvent& event)
{
Destroy();
}

View File

@@ -0,0 +1,16 @@
#ifndef FILEVIEWERWINDOW_H
#define FILEVIEWERWINDOW_H
#include "layout.h"
class FileViewerWindow : public FileViewerWindowGen
{
public:
FileViewerWindow(
wxWindow* parent, const std::string& title, const Bytes& data);
private:
void OnClose(wxCloseEvent& event);
};
#endif

View File

@@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b)
// C++ code generated with wxFormBuilder (version 3.10.1-234-gd93c9fc0-dirty)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@@ -186,11 +186,15 @@ MainWindowGen::MainWindowGen( wxWindow* parent, wxWindowID id, const wxString& t
browseButton = new wxButton( idlePanel, wxID_ANY, wxT("Browse disk"), wxDefaultPosition, wxDefaultSize, 0 );
browseButton->SetBitmap( wxArtProvider::GetBitmap( wxART_FOLDER_OPEN, wxART_TOOLBAR ) );
browseButton->Enable( false );
browseButton->SetToolTip( wxT("Access the files on the disk directly without needing to image it.") );
gSizer9->Add( browseButton, 0, wxALL|wxEXPAND, 5 );
formatButton = new wxButton( idlePanel, wxID_ANY, wxT("Format disk"), wxDefaultPosition, wxDefaultSize, 0 );
formatButton->SetBitmap( wxArtProvider::GetBitmap( wxART_DELETE, wxART_BUTTON ) );
gSizer9->Add( formatButton, 0, wxALL|wxEXPAND, 5 );
fgSizer8->Add( gSizer9, 1, wxEXPAND, 5 );
@@ -206,7 +210,7 @@ MainWindowGen::MainWindowGen( wxWindow* parent, wxWindowID id, const wxString& t
wxBoxSizer* bSizer41;
bSizer41 = new wxBoxSizer( wxVERTICAL );
imagerToolbar = new wxToolBar( imagePanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_FLAT|wxTB_HORIZONTAL|wxTB_TEXT );
imagerToolbar = new wxAuiToolBar( imagePanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxAUI_TB_HORZ_LAYOUT|wxAUI_TB_TEXT );
imagerBackTool = imagerToolbar->AddTool( wxID_ANY, wxT("Back"), wxArtProvider::GetBitmap( wxART_GO_BACK, wxART_TOOLBAR ), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString, NULL );
imagerToolbar->Realize();
@@ -272,30 +276,68 @@ MainWindowGen::MainWindowGen( wxWindow* parent, wxWindowID id, const wxString& t
fgSizer23->SetFlexibleDirection( wxBOTH );
fgSizer23->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
browserToolbar = new wxToolBar( browsePanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_FLAT|wxTB_HORIZONTAL|wxTB_TEXT );
browserToolbar = new wxAuiToolBar( browsePanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxAUI_TB_HORZ_LAYOUT|wxAUI_TB_TEXT );
browserBackTool = browserToolbar->AddTool( wxID_ANY, wxT("Back"), wxArtProvider::GetBitmap( wxART_GO_BACK, wxART_TOOLBAR ), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString, NULL );
browserToolbar->AddSeparator();
browserInfoTool = browserToolbar->AddTool( wxID_ANY, wxT("Info"), wxArtProvider::GetBitmap( wxART_INFORMATION, wxART_TOOLBAR ), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString, NULL );
browserViewTool = browserToolbar->AddTool( wxID_ANY, wxT("View"), wxArtProvider::GetBitmap( wxART_FILE_OPEN, wxART_TOOLBAR ), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString, NULL );
browserSaveTool = browserToolbar->AddTool( wxID_ANY, wxT("Save"), wxArtProvider::GetBitmap( wxART_FILE_SAVE, wxART_TOOLBAR ), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString, NULL );
browserMoreMenuButton = browserToolbar->AddTool( wxID_ANY, wxT("More"), wxArtProvider::GetBitmap( wxART_PLUS, wxART_TOOLBAR ), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString, NULL );
browserToolbar->SetToolDropDown( browserMoreMenuButton->GetId(), true );
browserMoreMenu = new wxMenu();
browserAddMenuItem = new wxMenuItem( browserMoreMenu, wxID_ANY, wxString( wxT("Add file") ) , wxEmptyString, wxITEM_NORMAL );
browserMoreMenu->Append( browserAddMenuItem );
browserNewDirectoryMenuItem = new wxMenuItem( browserMoreMenu, wxID_ANY, wxString( wxT("New directory") ) , wxEmptyString, wxITEM_NORMAL );
#ifdef __WXMSW__
browserNewDirectoryMenuItem->SetBitmaps( wxNullBitmap );
#elif (defined( __WXGTK__ ) || defined( __WXOSX__ ))
browserNewDirectoryMenuItem->SetBitmap( wxNullBitmap );
#endif
browserMoreMenu->Append( browserNewDirectoryMenuItem );
browserRenameMenuItem = new wxMenuItem( browserMoreMenu, wxID_ANY, wxString( wxT("Move file") ) , wxEmptyString, wxITEM_NORMAL );
#ifdef __WXMSW__
browserRenameMenuItem->SetBitmaps( wxNullBitmap );
#elif (defined( __WXGTK__ ) || defined( __WXOSX__ ))
browserRenameMenuItem->SetBitmap( wxNullBitmap );
#endif
browserMoreMenu->Append( browserRenameMenuItem );
browserDeleteMenuItem = new wxMenuItem( browserMoreMenu, wxID_ANY, wxString( wxT("Delete file") ) , wxEmptyString, wxITEM_NORMAL );
#ifdef __WXMSW__
browserDeleteMenuItem->SetBitmaps( wxNullBitmap );
#elif (defined( __WXGTK__ ) || defined( __WXOSX__ ))
browserDeleteMenuItem->SetBitmap( wxNullBitmap );
#endif
browserMoreMenu->Append( browserDeleteMenuItem );
browserToolbar->Connect( browserMoreMenuButton->GetId(), wxEVT_COMMAND_AUITOOLBAR_TOOL_DROPDOWN, wxAuiToolBarEventHandler( MainWindowGen::browserMoreMenuButtonOnDropDownMenu ), NULL, this );
browserToolbar->AddSeparator();
browserFormatTool = browserToolbar->AddTool( wxID_ANY, wxT("Format"), wxArtProvider::GetBitmap( wxART_DELETE, wxART_TOOLBAR ), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString, NULL );
browserToolbar->Realize();
fgSizer23->Add( browserToolbar, 0, wxEXPAND, 5 );
m_scrolledWindow1 = new wxScrolledWindow( browsePanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL );
m_scrolledWindow1->SetScrollRate( 5, 5 );
wxGridSizer* gSizer13;
gSizer13 = new wxGridSizer( 1, 1, 0, 0 );
browserTree = new wxDataViewCtrl( browsePanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_SINGLE );
m_dataViewColumn1 = browserTree->AppendIconTextColumn( wxT("Filename"), 0, wxDATAVIEW_CELL_EDITABLE, 250, static_cast<wxAlignment>(wxALIGN_LEFT), wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE );
m_dataViewColumn2 = browserTree->AppendTextColumn( wxT("Size"), 1, wxDATAVIEW_CELL_INERT, 100, static_cast<wxAlignment>(wxALIGN_RIGHT), wxDATAVIEW_COL_RESIZABLE );
m_dataViewColumn3 = browserTree->AppendTextColumn( wxT("Mode"), 2, wxDATAVIEW_CELL_INERT, -1, static_cast<wxAlignment>(wxALIGN_LEFT), wxDATAVIEW_COL_RESIZABLE );
fgSizer23->Add( browserTree, 0, wxALL|wxEXPAND, 5 );
browserView = new wxDataViewCtrl( m_scrolledWindow1, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
browserFilenameColumn = browserView->AppendTextColumn( wxT("Filename"), 0, wxDATAVIEW_CELL_INERT, -1, static_cast<wxAlignment>(wxALIGN_LEFT), wxDATAVIEW_COL_RESIZABLE );
browserModeColumn = browserView->AppendTextColumn( wxT("Mode"), 1, wxDATAVIEW_CELL_INERT, -1, static_cast<wxAlignment>(wxALIGN_LEFT), wxDATAVIEW_COL_RESIZABLE );
browserLengthColumn = browserView->AppendTextColumn( wxT("Length"), 2, wxDATAVIEW_CELL_INERT, -1, static_cast<wxAlignment>(wxALIGN_LEFT), wxDATAVIEW_COL_RESIZABLE );
browserExtraColumn = browserView->AppendTextColumn( wxT("Additional properties"), 0, wxDATAVIEW_CELL_INERT, -1, static_cast<wxAlignment>(wxALIGN_LEFT), wxDATAVIEW_COL_RESIZABLE );
gSizer13->Add( browserView, 0, wxEXPAND, 5 );
m_scrolledWindow1->SetSizer( gSizer13 );
m_scrolledWindow1->Layout();
gSizer13->Fit( m_scrolledWindow1 );
fgSizer23->Add( m_scrolledWindow1, 1, wxEXPAND | wxALL, 5 );
diskSpaceGauge = new wxGauge( browsePanel, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL|wxGA_SMOOTH );
diskSpaceGauge->SetValue( 0 );
fgSizer23->Add( diskSpaceGauge, 0, wxALL|wxEXPAND, 5 );
wxGridSizer* gSizer12;
gSizer12 = new wxGridSizer( 0, 2, 0, 0 );
@@ -313,6 +355,10 @@ MainWindowGen::MainWindowGen( wxWindow* parent, wxWindowID id, const wxString& t
fgSizer23->Add( gSizer12, 1, wxEXPAND, 5 );
m_staticText12 = new wxStaticText( browsePanel, wxID_ANY, wxT("No changes will be written until the 'commit' button is pressed."), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL );
m_staticText12->Wrap( -1 );
fgSizer23->Add( m_staticText12, 0, wxALL|wxEXPAND, 5 );
browsePanel->SetSizer( fgSizer23 );
browsePanel->Layout();
@@ -345,11 +391,29 @@ MainWindowGen::MainWindowGen( wxWindow* parent, wxWindowID id, const wxString& t
customConfigurationButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnCustomConfigurationButton ), NULL, this );
readButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnReadButton ), NULL, this );
writeButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnWriteButton ), NULL, this );
browseButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowseButton ), NULL, this );
formatButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnFormatButton ), NULL, this );
this->Connect( imagerBackTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBackButton ) );
imagerSaveImageButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnSaveImageButton ), NULL, this );
imagerSaveFluxButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnSaveFluxButton ), NULL, this );
imagerGoAgainButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnImagerGoAgainButton ), NULL, this );
this->Connect( browserBackTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBackButton ) );
this->Connect( browserInfoTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserInfoButton ) );
this->Connect( browserViewTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserViewButton ) );
this->Connect( browserSaveTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserSaveButton ) );
browserMoreMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainWindowGen::OnBrowserAddMenuItem ), this, browserAddMenuItem->GetId());
browserMoreMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainWindowGen::OnBrowserNewDirectoryMenuItem ), this, browserNewDirectoryMenuItem->GetId());
browserMoreMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainWindowGen::OnBrowserRenameMenuItem ), this, browserRenameMenuItem->GetId());
browserMoreMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainWindowGen::OnBrowserDeleteMenuItem ), this, browserDeleteMenuItem->GetId());
this->Connect( browserFormatTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserFormatButton ) );
browserTree->Connect( wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG, wxDataViewEventHandler( MainWindowGen::OnBrowserBeginDrag ), NULL, this );
browserTree->Connect( wxEVT_COMMAND_DATAVIEW_ITEM_DROP, wxDataViewEventHandler( MainWindowGen::OnBrowserDrop ), NULL, this );
browserTree->Connect( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, wxDataViewEventHandler( MainWindowGen::OnBrowserDropPossible ), NULL, this );
browserTree->Connect( wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_DONE, wxDataViewEventHandler( MainWindowGen::OnBrowserFilenameChanged ), NULL, this );
browserTree->Connect( wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING, wxDataViewEventHandler( MainWindowGen::OnBrowserDirectoryExpanding ), NULL, this );
browserTree->Connect( wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler( MainWindowGen::OnBrowserSelectionChanged ), NULL, this );
browserDiscardButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserDiscardButton ), NULL, this );
browserCommitButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserCommitButton ), NULL, this );
}
MainWindowGen::~MainWindowGen()
@@ -368,12 +432,27 @@ MainWindowGen::~MainWindowGen()
customConfigurationButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnCustomConfigurationButton ), NULL, this );
readButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnReadButton ), NULL, this );
writeButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnWriteButton ), NULL, this );
browseButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowseButton ), NULL, this );
formatButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnFormatButton ), NULL, this );
this->Disconnect( imagerBackTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBackButton ) );
imagerSaveImageButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnSaveImageButton ), NULL, this );
imagerSaveFluxButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnSaveFluxButton ), NULL, this );
imagerGoAgainButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnImagerGoAgainButton ), NULL, this );
this->Disconnect( browserBackTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBackButton ) );
this->Disconnect( browserInfoTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserInfoButton ) );
this->Disconnect( browserViewTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserViewButton ) );
this->Disconnect( browserSaveTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserSaveButton ) );
this->Disconnect( browserFormatTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserFormatButton ) );
browserTree->Disconnect( wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG, wxDataViewEventHandler( MainWindowGen::OnBrowserBeginDrag ), NULL, this );
browserTree->Disconnect( wxEVT_COMMAND_DATAVIEW_ITEM_DROP, wxDataViewEventHandler( MainWindowGen::OnBrowserDrop ), NULL, this );
browserTree->Disconnect( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, wxDataViewEventHandler( MainWindowGen::OnBrowserDropPossible ), NULL, this );
browserTree->Disconnect( wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_DONE, wxDataViewEventHandler( MainWindowGen::OnBrowserFilenameChanged ), NULL, this );
browserTree->Disconnect( wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING, wxDataViewEventHandler( MainWindowGen::OnBrowserDirectoryExpanding ), NULL, this );
browserTree->Disconnect( wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler( MainWindowGen::OnBrowserSelectionChanged ), NULL, this );
browserDiscardButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserDiscardButton ), NULL, this );
browserCommitButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnBrowserCommitButton ), NULL, this );
delete browserMoreMenu;
}
TextViewerWindowGen::TextViewerWindowGen( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
@@ -507,3 +586,301 @@ TextEditorWindowGen::~TextEditorWindowGen()
m_sdbSizer2Save->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( TextEditorWindowGen::OnSave ), NULL, this );
}
FileViewerWindowGen::FileViewerWindowGen( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxFlexGridSizer* fgSizer8;
fgSizer8 = new wxFlexGridSizer( 0, 1, 0, 0 );
fgSizer8->AddGrowableCol( 0 );
fgSizer8->AddGrowableRow( 0 );
fgSizer8->SetFlexibleDirection( wxBOTH );
fgSizer8->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_ALL );
m_notebook1 = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
m_panel8 = new wxPanel( m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxGridSizer* gSizer101;
gSizer101 = new wxGridSizer( 1, 1, 0, 0 );
textControl = new wxTextCtrl( m_panel8, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY );
textControl->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
gSizer101->Add( textControl, 0, wxALL|wxEXPAND, 5 );
m_panel8->SetSizer( gSizer101 );
m_panel8->Layout();
gSizer101->Fit( m_panel8 );
m_notebook1->AddPage( m_panel8, wxT("Text"), false );
m_panel7 = new wxPanel( m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxGridSizer* gSizer10;
gSizer10 = new wxGridSizer( 1, 1, 0, 0 );
hexControl = new wxTextCtrl( m_panel7, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY );
hexControl->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString ) );
gSizer10->Add( hexControl, 0, wxALL|wxEXPAND, 5 );
m_panel7->SetSizer( gSizer10 );
m_panel7->Layout();
gSizer10->Fit( m_panel7 );
m_notebook1->AddPage( m_panel7, wxT("Hex"), false );
fgSizer8->Add( m_notebook1, 1, wxEXPAND | wxALL, 5 );
m_sdbSizer2 = new wxStdDialogButtonSizer();
m_sdbSizer2OK = new wxButton( this, wxID_OK );
m_sdbSizer2->AddButton( m_sdbSizer2OK );
m_sdbSizer2->Realize();
fgSizer8->Add( m_sdbSizer2, 1, wxALL|wxEXPAND, 5 );
this->SetSizer( fgSizer8 );
this->Layout();
this->Centre( wxBOTH );
// Connect Events
m_sdbSizer2OK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FileViewerWindowGen::OnClose ), NULL, this );
}
FileViewerWindowGen::~FileViewerWindowGen()
{
// Disconnect Events
m_sdbSizer2OK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FileViewerWindowGen::OnClose ), NULL, this );
}
GetfileDialog::GetfileDialog( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxGridBagSizer* gbSizer1;
gbSizer1 = new wxGridBagSizer( 0, 0 );
gbSizer1->SetFlexibleDirection( wxBOTH );
gbSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_staticText7 = new wxStaticText( this, wxID_ANY, wxT("File on disk:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText7->Wrap( -1 );
gbSizer1->Add( m_staticText7, wxGBPosition( 0, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
filenameText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 300,-1 ), wxTE_READONLY );
filenameText->Enable( false );
gbSizer1->Add( filenameText, wxGBPosition( 0, 1 ), wxGBSpan( 1, 1 ), wxALL|wxEXPAND, 5 );
m_staticText9 = new wxStaticText( this, wxID_ANY, wxT("File to save as:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText9->Wrap( -1 );
gbSizer1->Add( m_staticText9, wxGBPosition( 1, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
targetFilePicker = new wxFilePickerCtrl( this, wxID_ANY, wxEmptyString, wxT("Select a file"), wxT("*.*"), wxDefaultPosition, wxDefaultSize, wxFLP_OVERWRITE_PROMPT|wxFLP_SAVE|wxFLP_USE_TEXTCTRL );
gbSizer1->Add( targetFilePicker, wxGBPosition( 1, 1 ), wxGBSpan( 1, 1 ), wxALL|wxEXPAND, 5 );
buttons_ = new wxStdDialogButtonSizer();
buttons_OK = new wxButton( this, wxID_OK );
buttons_->AddButton( buttons_OK );
buttons_Cancel = new wxButton( this, wxID_CANCEL );
buttons_->AddButton( buttons_Cancel );
buttons_->Realize();
gbSizer1->Add( buttons_, wxGBPosition( 2, 0 ), wxGBSpan( 1, 2 ), wxALL|wxEXPAND, 5 );
this->SetSizer( gbSizer1 );
this->Layout();
gbSizer1->Fit( this );
this->Centre( wxBOTH );
}
GetfileDialog::~GetfileDialog()
{
}
FileConflictDialog::FileConflictDialog( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxGridBagSizer* gbSizer1;
gbSizer1 = new wxGridBagSizer( 0, 0 );
gbSizer1->SetFlexibleDirection( wxBOTH );
gbSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_staticText91 = new wxStaticText( this, wxID_ANY, wxT("That name is already in use.\nPlease specify another:"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL );
m_staticText91->Wrap( -1 );
gbSizer1->Add( m_staticText91, wxGBPosition( 0, 0 ), wxGBSpan( 1, 2 ), wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
m_staticText7 = new wxStaticText( this, wxID_ANY, wxT("Old name:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText7->Wrap( -1 );
gbSizer1->Add( m_staticText7, wxGBPosition( 1, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
newNameText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 300,-1 ), 0 );
gbSizer1->Add( newNameText, wxGBPosition( 2, 1 ), wxGBSpan( 1, 1 ), wxALL|wxEXPAND, 5 );
m_staticText9 = new wxStaticText( this, wxID_ANY, wxT("New name:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText9->Wrap( -1 );
gbSizer1->Add( m_staticText9, wxGBPosition( 2, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
oldNameText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY );
oldNameText->Enable( false );
gbSizer1->Add( oldNameText, wxGBPosition( 1, 1 ), wxGBSpan( 1, 1 ), wxALL|wxEXPAND, 5 );
buttons_ = new wxStdDialogButtonSizer();
buttons_OK = new wxButton( this, wxID_OK );
buttons_->AddButton( buttons_OK );
buttons_Cancel = new wxButton( this, wxID_CANCEL );
buttons_->AddButton( buttons_Cancel );
buttons_->Realize();
gbSizer1->Add( buttons_, wxGBPosition( 3, 0 ), wxGBSpan( 1, 2 ), wxALL|wxEXPAND, 5 );
this->SetSizer( gbSizer1 );
this->Layout();
gbSizer1->Fit( this );
this->Centre( wxBOTH );
}
FileConflictDialog::~FileConflictDialog()
{
}
FileRenameDialog::FileRenameDialog( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxGridBagSizer* gbSizer1;
gbSizer1 = new wxGridBagSizer( 0, 0 );
gbSizer1->SetFlexibleDirection( wxBOTH );
gbSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_staticText91 = new wxStaticText( this, wxID_ANY, wxT("Please specify the new name\n(and path) of the file."), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL );
m_staticText91->Wrap( -1 );
gbSizer1->Add( m_staticText91, wxGBPosition( 0, 0 ), wxGBSpan( 1, 2 ), wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
m_staticText7 = new wxStaticText( this, wxID_ANY, wxT("Old name:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText7->Wrap( -1 );
gbSizer1->Add( m_staticText7, wxGBPosition( 1, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
newNameText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 300,-1 ), 0 );
gbSizer1->Add( newNameText, wxGBPosition( 2, 1 ), wxGBSpan( 1, 1 ), wxALL|wxEXPAND, 5 );
m_staticText9 = new wxStaticText( this, wxID_ANY, wxT("New name:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText9->Wrap( -1 );
gbSizer1->Add( m_staticText9, wxGBPosition( 2, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
oldNameText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY );
oldNameText->Enable( false );
gbSizer1->Add( oldNameText, wxGBPosition( 1, 1 ), wxGBSpan( 1, 1 ), wxALL|wxEXPAND, 5 );
buttons_ = new wxStdDialogButtonSizer();
buttons_OK = new wxButton( this, wxID_OK );
buttons_->AddButton( buttons_OK );
buttons_Cancel = new wxButton( this, wxID_CANCEL );
buttons_->AddButton( buttons_Cancel );
buttons_->Realize();
gbSizer1->Add( buttons_, wxGBPosition( 3, 0 ), wxGBSpan( 1, 2 ), wxALL|wxEXPAND, 5 );
this->SetSizer( gbSizer1 );
this->Layout();
gbSizer1->Fit( this );
this->Centre( wxBOTH );
}
FileRenameDialog::~FileRenameDialog()
{
}
CreateDirectoryDialog::CreateDirectoryDialog( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxGridBagSizer* gbSizer1;
gbSizer1 = new wxGridBagSizer( 0, 0 );
gbSizer1->SetFlexibleDirection( wxBOTH );
gbSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_staticText91 = new wxStaticText( this, wxID_ANY, wxT("Please specify the name (and path)\nof the new directory."), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL );
m_staticText91->Wrap( -1 );
gbSizer1->Add( m_staticText91, wxGBPosition( 0, 0 ), wxGBSpan( 1, 2 ), wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
newNameText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 300,-1 ), 0 );
gbSizer1->Add( newNameText, wxGBPosition( 1, 1 ), wxGBSpan( 1, 1 ), wxALL|wxEXPAND, 5 );
m_staticText9 = new wxStaticText( this, wxID_ANY, wxT("Name:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText9->Wrap( -1 );
gbSizer1->Add( m_staticText9, wxGBPosition( 1, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
buttons_ = new wxStdDialogButtonSizer();
buttons_OK = new wxButton( this, wxID_OK );
buttons_->AddButton( buttons_OK );
buttons_Cancel = new wxButton( this, wxID_CANCEL );
buttons_->AddButton( buttons_Cancel );
buttons_->Realize();
gbSizer1->Add( buttons_, wxGBPosition( 2, 0 ), wxGBSpan( 1, 2 ), wxALL|wxEXPAND, 5 );
this->SetSizer( gbSizer1 );
this->Layout();
gbSizer1->Fit( this );
this->Centre( wxBOTH );
}
CreateDirectoryDialog::~CreateDirectoryDialog()
{
}
FormatDialog::FormatDialog( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxGridBagSizer* gbSizer1;
gbSizer1 = new wxGridBagSizer( 0, 0 );
gbSizer1->SetFlexibleDirection( wxBOTH );
gbSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_staticText91 = new wxStaticText( this, wxID_ANY, wxT("This will erase the entire disk. (But remember that\nyou can always undo changes until you press the 'commit changes' button.)"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL );
m_staticText91->Wrap( -1 );
gbSizer1->Add( m_staticText91, wxGBPosition( 0, 0 ), wxGBSpan( 1, 2 ), wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
m_staticText7 = new wxStaticText( this, wxID_ANY, wxT("Volume name:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText7->Wrap( -1 );
gbSizer1->Add( m_staticText7, wxGBPosition( 1, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
volumeNameText = new wxTextCtrl( this, wxID_ANY, wxT("FluxEngineDisk"), wxDefaultPosition, wxDefaultSize, 0 );
gbSizer1->Add( volumeNameText, wxGBPosition( 1, 1 ), wxGBSpan( 1, 1 ), wxALL|wxEXPAND, 5 );
quickFormatCheckBox = new wxCheckBox( this, wxID_ANY, wxT("Quick format: if the disk is not already correctly formatted,\nvery bad things will happen!"), wxDefaultPosition, wxDefaultSize, 0 );
gbSizer1->Add( quickFormatCheckBox, wxGBPosition( 2, 1 ), wxGBSpan( 1, 1 ), wxALL|wxEXPAND, 5 );
buttons_ = new wxStdDialogButtonSizer();
buttons_OK = new wxButton( this, wxID_OK );
buttons_->AddButton( buttons_OK );
buttons_Cancel = new wxButton( this, wxID_CANCEL );
buttons_->AddButton( buttons_Cancel );
buttons_->Realize();
gbSizer1->Add( buttons_, wxGBPosition( 3, 0 ), wxGBSpan( 1, 2 ), wxALL|wxEXPAND, 5 );
this->SetSizer( gbSizer1 );
this->Layout();
gbSizer1->Fit( this );
this->Centre( wxBOTH );
}
FormatDialog::~FormatDialog()
{
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b)
// C++ code generated with wxFormBuilder (version 3.10.1-234-gd93c9fc0-dirty)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@@ -28,15 +28,19 @@
#include <wx/filepicker.h>
#include <wx/button.h>
#include <wx/scrolwin.h>
#include <wx/toolbar.h>
#include <wx/aui/aui.h>
#include <wx/aui/auibar.h>
#include "visualisationcontrol.h"
#include <wx/dataview.h>
#include <wx/gauge.h>
#include <wx/simplebook.h>
#include <wx/frame.h>
#include <wx/textctrl.h>
#include <wx/dialog.h>
#include "fluxviewercontrol.h"
#include <wx/scrolbar.h>
#include <wx/notebook.h>
#include <wx/gbsizer.h>
///////////////////////////////////////////////////////////////////////////
@@ -73,25 +77,36 @@ class MainWindowGen : public wxFrame
wxButton* readButton;
wxButton* writeButton;
wxButton* browseButton;
wxButton* formatButton;
wxPanel* imagePanel;
wxToolBar* imagerToolbar;
wxToolBarToolBase* imagerBackTool;
wxAuiToolBar* imagerToolbar;
wxAuiToolBarItem* imagerBackTool;
VisualisationControl* visualiser;
wxButton* imagerSaveImageButton;
wxButton* imagerSaveFluxButton;
wxStaticText* m_staticText4;
wxButton* imagerGoAgainButton;
wxPanel* browsePanel;
wxToolBar* browserToolbar;
wxToolBarToolBase* browserBackTool;
wxScrolledWindow* m_scrolledWindow1;
wxDataViewCtrl* browserView;
wxDataViewColumn* browserFilenameColumn;
wxDataViewColumn* browserModeColumn;
wxDataViewColumn* browserLengthColumn;
wxDataViewColumn* browserExtraColumn;
wxAuiToolBar* browserToolbar;
wxAuiToolBarItem* browserBackTool;
wxAuiToolBarItem* browserInfoTool;
wxAuiToolBarItem* browserViewTool;
wxAuiToolBarItem* browserSaveTool;
wxAuiToolBarItem* browserMoreMenuButton;
wxMenu* browserMoreMenu;
wxMenuItem* browserAddMenuItem;
wxMenuItem* browserNewDirectoryMenuItem;
wxMenuItem* browserRenameMenuItem;
wxMenuItem* browserDeleteMenuItem;
wxAuiToolBarItem* browserFormatTool;
wxDataViewCtrl* browserTree;
wxDataViewColumn* m_dataViewColumn1;
wxDataViewColumn* m_dataViewColumn2;
wxDataViewColumn* m_dataViewColumn3;
wxGauge* diskSpaceGauge;
wxButton* browserDiscardButton;
wxButton* browserCommitButton;
wxStaticText* m_staticText12;
// Virtual event handlers, override them in your derived class
virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
@@ -105,18 +120,49 @@ class MainWindowGen : public wxFrame
virtual void OnCustomConfigurationButton( wxCommandEvent& event ) { event.Skip(); }
virtual void OnReadButton( wxCommandEvent& event ) { event.Skip(); }
virtual void OnWriteButton( wxCommandEvent& event ) { event.Skip(); }
virtual void OnBrowseButton( wxCommandEvent& event ) { event.Skip(); }
virtual void OnFormatButton( wxCommandEvent& event ) { event.Skip(); }
virtual void OnBackButton( wxCommandEvent& event ) { event.Skip(); }
virtual void OnSaveImageButton( wxCommandEvent& event ) { event.Skip(); }
virtual void OnSaveFluxButton( wxCommandEvent& event ) { event.Skip(); }
virtual void OnImagerGoAgainButton( wxCommandEvent& event ) { event.Skip(); }
virtual void OnBrowserInfoButton( wxCommandEvent& event ) { event.Skip(); }
virtual void OnBrowserViewButton( wxCommandEvent& event ) { event.Skip(); }
virtual void OnBrowserSaveButton( wxCommandEvent& event ) { event.Skip(); }
virtual void OnBrowserAddMenuItem( wxCommandEvent& event ) { event.Skip(); }
virtual void OnBrowserNewDirectoryMenuItem( wxCommandEvent& event ) { event.Skip(); }
virtual void OnBrowserRenameMenuItem( wxCommandEvent& event ) { event.Skip(); }
virtual void OnBrowserDeleteMenuItem( wxCommandEvent& event ) { event.Skip(); }
virtual void OnBrowserFormatButton( wxCommandEvent& event ) { event.Skip(); }
virtual void OnBrowserBeginDrag( wxDataViewEvent& event ) { event.Skip(); }
virtual void OnBrowserDrop( wxDataViewEvent& event ) { event.Skip(); }
virtual void OnBrowserDropPossible( wxDataViewEvent& event ) { event.Skip(); }
virtual void OnBrowserFilenameChanged( wxDataViewEvent& event ) { event.Skip(); }
virtual void OnBrowserDirectoryExpanding( wxDataViewEvent& event ) { event.Skip(); }
virtual void OnBrowserSelectionChanged( wxDataViewEvent& event ) { event.Skip(); }
virtual void OnBrowserDiscardButton( wxCommandEvent& event ) { event.Skip(); }
virtual void OnBrowserCommitButton( wxCommandEvent& event ) { event.Skip(); }
public:
MainWindowGen( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("FluxEngine"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 819,607 ), long style = wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER|wxFULL_REPAINT_ON_RESIZE|wxTAB_TRAVERSAL );
MainWindowGen( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("FluxEngine"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 616,607 ), long style = wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER|wxFULL_REPAINT_ON_RESIZE|wxTAB_TRAVERSAL );
~MainWindowGen();
void browserMoreMenuButtonOnDropDownMenu( wxAuiToolBarEvent &event )
{
if ( event.IsDropDownClicked() )
{
browserToolbar->SetToolSticky( event.GetId(), true );
wxRect rect = browserToolbar->GetToolRect( event.GetId() );
wxPoint pt = browserToolbar->ClientToScreen( rect.GetBottomLeft() );
pt = ScreenToClient( pt );
browserToolbar->PopupMenu( browserMoreMenu, pt );
browserToolbar->SetToolSticky( event.GetId(), false );
}
}
};
///////////////////////////////////////////////////////////////////////////////
@@ -138,7 +184,7 @@ class TextViewerWindowGen : public wxDialog
public:
TextViewerWindowGen( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 208,143 ), long style = wxCLOSE_BOX|wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
TextViewerWindowGen( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 208,143 ), long style = wxCLOSE_BOX|wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER );
~TextViewerWindowGen();
@@ -164,7 +210,7 @@ class FluxViewerWindowGen : public wxDialog
public:
FluxViewerWindowGen( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 400,200 ), long style = wxCLOSE_BOX|wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
FluxViewerWindowGen( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 400,200 ), long style = wxCLOSE_BOX|wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER );
~FluxViewerWindowGen();
@@ -191,9 +237,158 @@ class TextEditorWindowGen : public wxDialog
public:
TextEditorWindowGen( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxCLOSE_BOX|wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
TextEditorWindowGen( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxCLOSE_BOX|wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER );
~TextEditorWindowGen();
};
///////////////////////////////////////////////////////////////////////////////
/// Class FileViewerWindowGen
///////////////////////////////////////////////////////////////////////////////
class FileViewerWindowGen : public wxDialog
{
private:
protected:
wxNotebook* m_notebook1;
wxPanel* m_panel8;
wxTextCtrl* textControl;
wxPanel* m_panel7;
wxTextCtrl* hexControl;
wxStdDialogButtonSizer* m_sdbSizer2;
wxButton* m_sdbSizer2OK;
// Virtual event handlers, override them in your derived class
virtual void OnClose( wxCommandEvent& event ) { event.Skip(); }
public:
FileViewerWindowGen( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 408,269 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER );
~FileViewerWindowGen();
};
///////////////////////////////////////////////////////////////////////////////
/// Class GetfileDialog
///////////////////////////////////////////////////////////////////////////////
class GetfileDialog : public wxDialog
{
private:
protected:
wxStaticText* m_staticText7;
wxStaticText* m_staticText9;
public:
wxTextCtrl* filenameText;
wxFilePickerCtrl* targetFilePicker;
wxStdDialogButtonSizer* buttons_;
wxButton* buttons_OK;
wxButton* buttons_Cancel;
GetfileDialog( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Copy file off disk"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE );
~GetfileDialog();
};
///////////////////////////////////////////////////////////////////////////////
/// Class FileConflictDialog
///////////////////////////////////////////////////////////////////////////////
class FileConflictDialog : public wxDialog
{
private:
protected:
wxStaticText* m_staticText91;
wxStaticText* m_staticText7;
wxStaticText* m_staticText9;
public:
wxTextCtrl* newNameText;
wxTextCtrl* oldNameText;
wxStdDialogButtonSizer* buttons_;
wxButton* buttons_OK;
wxButton* buttons_Cancel;
FileConflictDialog( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Filename conflict"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE );
~FileConflictDialog();
};
///////////////////////////////////////////////////////////////////////////////
/// Class FileRenameDialog
///////////////////////////////////////////////////////////////////////////////
class FileRenameDialog : public wxDialog
{
private:
protected:
wxStaticText* m_staticText91;
wxStaticText* m_staticText7;
wxStaticText* m_staticText9;
public:
wxTextCtrl* newNameText;
wxTextCtrl* oldNameText;
wxStdDialogButtonSizer* buttons_;
wxButton* buttons_OK;
wxButton* buttons_Cancel;
FileRenameDialog( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Rename or move file"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE );
~FileRenameDialog();
};
///////////////////////////////////////////////////////////////////////////////
/// Class CreateDirectoryDialog
///////////////////////////////////////////////////////////////////////////////
class CreateDirectoryDialog : public wxDialog
{
private:
protected:
wxStaticText* m_staticText91;
wxStaticText* m_staticText9;
public:
wxTextCtrl* newNameText;
wxStdDialogButtonSizer* buttons_;
wxButton* buttons_OK;
wxButton* buttons_Cancel;
CreateDirectoryDialog( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Create new directory"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE );
~CreateDirectoryDialog();
};
///////////////////////////////////////////////////////////////////////////////
/// Class FormatDialog
///////////////////////////////////////////////////////////////////////////////
class FormatDialog : public wxDialog
{
private:
protected:
wxStaticText* m_staticText91;
wxStaticText* m_staticText7;
public:
wxTextCtrl* volumeNameText;
wxCheckBox* quickFormatCheckBox;
wxStdDialogButtonSizer* buttons_;
wxButton* buttons_OK;
wxButton* buttons_Cancel;
FormatDialog( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Format disk"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE );
~FormatDialog();
};

View File

@@ -15,11 +15,15 @@
#include "utils.h"
#include "fluxviewerwindow.h"
#include "textviewerwindow.h"
#include "fileviewerwindow.h"
#include "texteditorwindow.h"
#include "filesystemmodel.h"
#include "customstatusbar.h"
#include "lib/vfs/vfs.h"
#include <google/protobuf/text_format.h>
#include <wx/config.h>
#include <wx/aboutdlg.h>
#include <deque>
extern const std::map<std::string, std::string> formats;
@@ -38,10 +42,25 @@ const std::string DEFAULT_EXTRA_CONFIGURATION =
"# or the name of a built-in configuration, or the filename\n"
"# of a text proto file. Or a comment, of course.\n\n";
const std::string DND_TYPE = "fluxengine.files";
class CancelException
{
};
class MainWindow : public MainWindowGen
{
private:
class FilesystemOperation;
public:
MainWindow(): MainWindowGen(nullptr), _config("FluxEngine")
MainWindow():
MainWindowGen(nullptr),
/* This is wrong. Apparently the wxDataViewCtrl doesn't work properly
* with DnD unless the format is wxDF_UNICODETEXT. It should be a custom
* value. */
_dndFormat(wxDF_UNICODETEXT),
_config("FluxEngine")
{
Logger::setLogger(
[&](std::shared_ptr<const AnyLogMessage> message)
@@ -96,6 +115,26 @@ public:
emergencyStop = true;
});
_filesystemModel = FilesystemModel::Associate(browserTree);
/* This is a bug workaround for an issue in wxformbuilder's generated
* code; see https://github.com/wxFormBuilder/wxFormBuilder/pull/758.
* The default handler for the submenu doesn't allow events to fire on
* the button itself, so we have to override it with our own version. */
browserToolbar->Connect(browserMoreMenuButton->GetId(),
wxEVT_COMMAND_AUITOOLBAR_TOOL_DROPDOWN,
wxAuiToolBarEventHandler(MainWindow::OnBrowserMoreMenuButton),
NULL,
this);
/* This is a bug workaround for an issue where the calculation of the
* item being dropped on is wrong due to the header not being taken into
* account. See https://forums.wxwidgets.org/viewtopic.php?t=44752. */
browserTree->EnableDragSource(_dndFormat);
browserTree->EnableDropTarget(_dndFormat);
/* I have no idea why this is necessary, but on Windows things aren't
* laid out correctly without it. */
@@ -193,8 +232,8 @@ public:
void OnControlsChanged(wxFileDirPickerEvent& event)
{
SaveConfig();
UpdateState();
wxCommandEvent e;
OnControlsChanged(e);
}
void OnReadButton(wxCommandEvent&)
@@ -257,6 +296,8 @@ public:
ImageReader::updateConfigForFilename(
config.mutable_image_reader(), filename.ToStdString());
ImageWriter::updateConfigForFilename(
config.mutable_image_writer(), filename.ToStdString());
visualiser->Clear();
_currentDisk = nullptr;
@@ -394,6 +435,605 @@ public:
UpdateState();
}
/* --- Browser ---------------------------------------------------------- */
void OnBrowseButton(wxCommandEvent& event) override
{
try
{
PrepareConfig();
visualiser->Clear();
_filesystemModel->Clear(Path());
_currentDisk = nullptr;
_state = STATE_BROWSING_WORKING;
UpdateState();
ShowConfig();
QueueBrowserOperation(
[this]()
{
_filesystem = Filesystem::createFilesystemFromConfig();
_filesystemCapabilities = _filesystem->capabilities();
_filesystemIsReadOnly = _filesystem->isReadOnly();
runOnUiThread(
[&]()
{
RepopulateBrowser();
});
});
}
catch (const ErrorException& e)
{
wxMessageBox(e.message, "Error", wxOK | wxICON_ERROR);
_state = STATE_IDLE;
}
}
void OnFormatButton(wxCommandEvent& event) override
{
try
{
PrepareConfig();
visualiser->Clear();
_filesystemModel->Clear(Path());
_currentDisk = nullptr;
_state = STATE_BROWSING_WORKING;
UpdateState();
ShowConfig();
QueueBrowserOperation(
[this]()
{
_filesystem = Filesystem::createFilesystemFromConfig();
_filesystemCapabilities = _filesystem->capabilities();
_filesystemIsReadOnly = _filesystem->isReadOnly();
runOnUiThread(
[&]()
{
wxCommandEvent e;
OnBrowserFormatButton(e);
});
});
}
catch (const ErrorException& e)
{
wxMessageBox(e.message, "Error", wxOK | wxICON_ERROR);
_state = STATE_IDLE;
}
}
void OnBrowserMoreMenuButton(wxAuiToolBarEvent& event)
{
browserToolbar->SetToolSticky(event.GetId(), true);
wxRect rect = browserToolbar->GetToolRect(event.GetId());
wxPoint pt = browserToolbar->ClientToScreen(rect.GetBottomLeft());
pt = ScreenToClient(pt);
browserToolbar->PopupMenu(browserMoreMenu, pt);
browserToolbar->SetToolSticky(event.GetId(), false);
}
void RepopulateBrowser(Path path = Path())
{
QueueBrowserOperation(
[this, path]()
{
auto files = _filesystem->list(path);
runOnUiThread(
[&]()
{
_filesystemModel->Clear(path);
for (auto& f : files)
_filesystemModel->Add(f);
auto node = _filesystemModel->Find(path);
if (node)
browserTree->Expand(node->item);
UpdateFilesystemData();
});
});
}
void UpdateFilesystemData()
{
QueueBrowserOperation(
[this]()
{
auto metadata = _filesystem->getMetadata();
_filesystemNeedsFlushing = _filesystem->needsFlushing();
runOnUiThread(
[&]()
{
try
{
uint32_t blockSize =
std::stoul(metadata.at(Filesystem::BLOCK_SIZE));
uint32_t totalBlocks = std::stoul(
metadata.at(Filesystem::TOTAL_BLOCKS));
uint32_t usedBlocks = std::stoul(
metadata.at(Filesystem::USED_BLOCKS));
diskSpaceGauge->Enable();
diskSpaceGauge->SetRange(totalBlocks * blockSize);
diskSpaceGauge->SetValue(usedBlocks * blockSize);
}
catch (const std::out_of_range& e)
{
diskSpaceGauge->Disable();
}
});
});
}
void OnBrowserDirectoryExpanding(wxDataViewEvent& event) override
{
auto node = _filesystemModel->Find(event.GetItem());
if (node && !node->populated && !node->populating)
{
node->populating = true;
RepopulateBrowser(node->dirent->path);
}
}
void OnBrowserInfoButton(wxCommandEvent&) override
{
auto item = browserTree->GetSelection();
auto node = _filesystemModel->Find(item);
std::stringstream ss;
ss << "File attributes for " << node->dirent->path.to_str() << ":\n\n";
for (const auto& e : node->dirent->attributes)
ss << e.first << "=" << quote(e.second) << "\n";
TextViewerWindow::Create(
this, node->dirent->path.to_str(), ss.str(), true)
->Show();
}
void OnBrowserViewButton(wxCommandEvent&) override
{
auto item = browserTree->GetSelection();
auto node = _filesystemModel->Find(item);
QueueBrowserOperation(
[this, node]()
{
auto bytes = _filesystem->getFile(node->dirent->path);
runOnUiThread(
[&]()
{
(new FileViewerWindow(
this, node->dirent->path.to_str(), bytes))
->Show();
});
});
}
void OnBrowserSaveButton(wxCommandEvent&) override
{
auto item = browserTree->GetSelection();
auto node = _filesystemModel->Find(item);
GetfileDialog d(this, wxID_ANY);
d.filenameText->SetValue(node->dirent->path.to_str());
d.targetFilePicker->SetFileName(wxFileName(node->dirent->filename));
d.targetFilePicker->SetFocus();
d.buttons_OK->SetDefault();
if (d.ShowModal() != wxID_OK)
return;
auto localPath = d.targetFilePicker->GetPath().ToStdString();
QueueBrowserOperation(
[this, node, localPath]()
{
auto bytes = _filesystem->getFile(node->dirent->path);
bytes.writeToFile(localPath);
});
}
/* Called from worker thread only! */
Path ResolveFileConflicts_WT(Path path)
{
do
{
try
{
_filesystem->getDirent(path);
}
catch (const FileNotFoundException& e)
{
break;
}
runOnUiThread(
[&]()
{
FileConflictDialog d(this, wxID_ANY);
d.oldNameText->SetValue(path.to_str());
d.newNameText->SetValue(path.to_str());
d.newNameText->SetFocus();
d.buttons_OK->SetDefault();
if (d.ShowModal() == wxID_OK)
path = Path(d.newNameText->GetValue().ToStdString());
else
path = Path("");
});
} while (!path.empty());
return path;
}
std::shared_ptr<FilesystemNode> GetTargetDirectoryNode(wxDataViewItem& item)
{
Path path;
if (item.IsOk())
{
auto node = _filesystemModel->Find(item);
if (!node)
return nullptr;
path = node->dirent->path;
}
auto node = _filesystemModel->Find(path);
if (!node)
return nullptr;
if (node->dirent->file_type != TYPE_DIRECTORY)
return _filesystemModel->Find(path.parent());
return node;
}
void OnBrowserAddMenuItem(wxCommandEvent&) override
{
auto item = browserTree->GetSelection();
auto dirNode = GetTargetDirectoryNode(item);
if (!dirNode)
return;
auto localPath = wxFileSelector("Choose the name of the file to add",
/* default_path= */ wxEmptyString,
/* default_filename= */ wxEmptyString,
/* default_extension= */ wxEmptyString,
/* wildcard= */ wxEmptyString,
/* flags= */ wxFD_OPEN | wxFD_FILE_MUST_EXIST)
.ToStdString();
if (localPath.empty())
return;
auto path = dirNode->dirent->path.concat(
wxFileName(localPath).GetFullName().ToStdString());
QueueBrowserOperation(
[this, path, localPath]() mutable
{
path = ResolveFileConflicts_WT(path);
if (path.empty())
return;
auto bytes = Bytes::readFromFile(localPath);
_filesystem->putFile(path, bytes);
auto dirent = _filesystem->getDirent(path);
runOnUiThread(
[&]()
{
_filesystemModel->Add(dirent);
UpdateFilesystemData();
});
});
}
void OnBrowserDeleteMenuItem(wxCommandEvent&) override
{
auto item = browserTree->GetSelection();
auto node = _filesystemModel->Find(item);
if (!node)
return;
QueueBrowserOperation(
[this, node]()
{
_filesystem->deleteFile(node->dirent->path);
runOnUiThread(
[&]()
{
_filesystemModel->Delete(node->dirent->path);
UpdateFilesystemData();
});
});
}
void OnBrowserFormatButton(wxCommandEvent&) override
{
FormatDialog d(this, wxID_ANY);
d.volumeNameText->SetFocus();
d.buttons_OK->SetDefault();
if (d.ShowModal() != wxID_OK)
return;
auto volumeName = d.volumeNameText->GetValue().ToStdString();
auto quickFormat = d.quickFormatCheckBox->GetValue();
QueueBrowserOperation(
[this, volumeName, quickFormat]()
{
_filesystem->discardChanges();
_filesystem->create(quickFormat, volumeName);
runOnUiThread(
[&]()
{
RepopulateBrowser();
});
});
}
void OnBrowserFilenameChanged(wxDataViewEvent& event)
{
if (!(_filesystem->capabilities() & Filesystem::OP_MOVE))
return;
auto node = _filesystemModel->Find(event.GetItem());
if (!node)
return;
if (node->newname.empty())
return;
if (node->newname == node->dirent->filename)
return;
QueueBrowserOperation(
[this, node]() mutable
{
auto oldPath = node->dirent->path;
auto newPath = oldPath.parent().concat(node->newname);
newPath = ResolveFileConflicts_WT(newPath);
if (newPath.empty())
return;
_filesystem->moveFile(oldPath, newPath);
auto dirent = _filesystem->getDirent(newPath);
runOnUiThread(
[&]()
{
_filesystemModel->Delete(oldPath);
_filesystemModel->Add(dirent);
UpdateFilesystemData();
});
});
}
void OnBrowserRenameMenuItem(wxCommandEvent& event)
{
auto item = browserTree->GetSelection();
auto node = _filesystemModel->Find(item);
FileRenameDialog d(this, wxID_ANY);
d.oldNameText->SetValue(node->dirent->path.to_str());
d.newNameText->SetValue(node->dirent->path.to_str());
d.newNameText->SetFocus();
d.buttons_OK->SetDefault();
if (d.ShowModal() != wxID_OK)
return;
ActuallyMoveFile(
node->dirent->path, Path(d.newNameText->GetValue().ToStdString()));
}
void ActuallyMoveFile(const Path& oldPath, Path newPath)
{
QueueBrowserOperation(
[this, oldPath, newPath]() mutable
{
newPath = ResolveFileConflicts_WT(newPath);
if (newPath.empty())
return;
_filesystem->moveFile(oldPath, newPath);
auto dirent = _filesystem->getDirent(newPath);
runOnUiThread(
[&]()
{
_filesystemModel->Delete(oldPath);
_filesystemModel->Add(dirent);
UpdateFilesystemData();
});
});
}
void OnBrowserNewDirectoryMenuItem(wxCommandEvent& event)
{
auto item = browserTree->GetSelection();
auto node = GetTargetDirectoryNode(item);
if (!node)
return;
auto path = node->dirent->path;
CreateDirectoryDialog d(this, wxID_ANY);
d.newNameText->SetValue(path.to_str() + "/");
d.newNameText->SetFocus();
d.buttons_OK->SetDefault();
if (d.ShowModal() != wxID_OK)
return;
auto newPath = Path(d.newNameText->GetValue().ToStdString());
QueueBrowserOperation(
[this, newPath]() mutable
{
newPath = ResolveFileConflicts_WT(newPath);
_filesystem->createDirectory(newPath);
auto dirent = _filesystem->getDirent(newPath);
runOnUiThread(
[&]()
{
_filesystemModel->Add(dirent);
UpdateFilesystemData();
});
});
}
void OnBrowserBeginDrag(wxDataViewEvent& event) override
{
auto item = browserTree->GetSelection();
if (!item.IsOk())
{
event.Veto();
return;
}
auto node = _filesystemModel->Find(item);
if (!node)
{
event.Veto();
return;
}
wxTextDataObject* obj = new wxTextDataObject();
obj->SetText(node->dirent->path.to_str());
event.SetDataObject(obj);
event.SetDataFormat(_dndFormat);
}
void OnBrowserDropPossible(wxDataViewEvent& event) override
{
if (event.GetDataFormat() != _dndFormat)
{
event.Veto();
return;
}
}
void OnBrowserDrop(wxDataViewEvent& event) override
{
try
{
if (event.GetDataFormat() != _dndFormat)
throw CancelException();
/* wxWidgets 3.0 data view DnD is borked. See
* https://forums.wxwidgets.org/viewtopic.php?t=44752. The hit
* detection is done against the wrong object, resulting in the
* header size not being taken into account, so we have to manually
* do hit detection correctly. */
auto* window = browserTree->GetMainWindow();
auto screenPos = wxGetMousePosition();
auto relPos = screenPos - window->GetScreenPosition();
wxDataViewItem item;
wxDataViewColumn* column;
browserTree->HitTest(relPos, item, column);
if (!item.IsOk())
throw CancelException();
auto destDirNode = GetTargetDirectoryNode(item);
if (!destDirNode)
throw CancelException();
auto destDirPath = destDirNode->dirent->path;
wxTextDataObject obj;
obj.SetData(_dndFormat, event.GetDataSize(), event.GetDataBuffer());
auto srcPath = Path(obj.GetText().ToStdString());
if (srcPath.empty())
throw CancelException();
ActuallyMoveFile(srcPath, destDirPath.concat(srcPath.back()));
}
catch (const CancelException& e)
{
event.Veto();
}
}
void OnBrowserCommitButton(wxCommandEvent&) override
{
QueueBrowserOperation(
[this]()
{
_filesystem->flushChanges();
UpdateFilesystemData();
});
}
void OnBrowserDiscardButton(wxCommandEvent&) override
{
QueueBrowserOperation(
[this]()
{
_filesystem->discardChanges();
runOnUiThread(
[&]()
{
RepopulateBrowser();
});
});
}
void QueueBrowserOperation(std::function<void()> f)
{
_filesystemQueue.push_back(f);
KickBrowserQueue();
}
void KickBrowserQueue()
{
bool running = wxGetApp().IsWorkerThreadRunning();
if (!running)
{
_state = STATE_BROWSING_WORKING;
_errorState = STATE_BROWSING_IDLE;
UpdateState();
runOnWorkerThread(
[this]()
{
for (;;)
{
std::function<void()> f;
runOnUiThread(
[&]()
{
UpdateState();
if (!_filesystemQueue.empty())
{
f = _filesystemQueue.front();
_filesystemQueue.pop_front();
}
});
if (!f)
break;
f();
}
runOnUiThread(
[&]()
{
_state = STATE_BROWSING_IDLE;
UpdateState();
});
});
}
}
void OnBrowserSelectionChanged(wxDataViewEvent& event) override
{
UpdateState();
}
/* --- Config management
* ------------------------------------------------ */
/* This sets the *global* config object. That's safe provided the worker
* thread isn't running, otherwise you'll get a race. */
void PrepareConfig()
@@ -508,6 +1148,7 @@ public:
_statusBar->HideProgressBar();
_statusBar->SetRightLabel("");
_state = _errorState;
_filesystemQueue.clear();
UpdateState();
},
@@ -690,8 +1331,6 @@ public:
void UpdateState()
{
bool running = wxGetApp().IsWorkerThreadRunning();
if (_state < STATE_IDLE__LAST)
{
dataNotebook->SetSelection(0);
@@ -724,6 +1363,36 @@ public:
else if (_state < STATE_BROWSING__LAST)
{
dataNotebook->SetSelection(2);
bool running = !_filesystemQueue.empty();
bool selection = browserTree->GetSelection().IsOk();
browserToolbar->EnableTool(
browserBackTool->GetId(), _state == STATE_BROWSING_IDLE);
uint32_t c = _filesystemCapabilities;
bool ro = _filesystemIsReadOnly;
bool needsFlushing = _filesystemNeedsFlushing;
browserToolbar->EnableTool(browserInfoTool->GetId(),
!running && (c & Filesystem::OP_GETDIRENT) && selection);
browserToolbar->EnableTool(browserViewTool->GetId(),
!running && (c & Filesystem::OP_GETFILE) && selection);
browserToolbar->EnableTool(browserSaveTool->GetId(),
!running && (c & Filesystem::OP_GETFILE) && selection);
browserMoreMenu->Enable(browserAddMenuItem->GetId(),
!running && !ro && (c & Filesystem::OP_PUTFILE));
browserMoreMenu->Enable(browserNewDirectoryMenuItem->GetId(),
!running && !ro && (c & Filesystem::OP_CREATEDIR));
browserMoreMenu->Enable(browserRenameMenuItem->GetId(),
!running && !ro && (c & Filesystem::OP_MOVE) && selection);
browserMoreMenu->Enable(browserDeleteMenuItem->GetId(),
!running && !ro && (c & Filesystem::OP_DELETE) && selection);
browserToolbar->EnableTool(browserFormatTool->GetId(),
!running && !ro && (c & Filesystem::OP_CREATE));
browserDiscardButton->Enable(!running && needsFlushing);
browserCommitButton->Enable(!running && needsFlushing);
}
Refresh();
@@ -785,11 +1454,66 @@ private:
STATE_BROWSING__LAST
};
class FilesystemOperation
{
public:
FilesystemOperation(
int operation, const Path& path, const wxDataViewItem& item):
operation(operation),
path(path),
item(item)
{
}
FilesystemOperation(int operation, const Path& path):
operation(operation),
path(path)
{
}
FilesystemOperation(
int operation, const Path& path, const std::string& local):
operation(operation),
path(path),
local(local)
{
}
FilesystemOperation(int operation,
const Path& path,
const wxDataViewItem& item,
wxString& local):
operation(operation),
path(path),
item(item),
local(local)
{
}
FilesystemOperation(int operation, const Path& path, wxString local):
operation(operation),
path(path),
local(local)
{
}
FilesystemOperation(int operation): operation(operation) {}
int operation;
Path path;
wxDataViewItem item;
std::string local;
std::string volumeName;
bool quickFormat;
};
wxConfig _config;
wxDataFormat _dndFormat;
std::vector<std::pair<std::string, std::unique_ptr<const ConfigProto>>>
_formats;
std::vector<std::unique_ptr<const CandidateDevice>> _devices;
int _state;
int _state = STATE_IDLE;
int _errorState;
int _selectedSource;
bool _dontSaveConfig = false;
@@ -799,6 +1523,12 @@ private:
std::unique_ptr<TextViewerWindow> _logWindow;
std::unique_ptr<TextViewerWindow> _configWindow;
std::string _extraConfiguration;
std::unique_ptr<Filesystem> _filesystem;
uint32_t _filesystemCapabilities;
bool _filesystemIsReadOnly;
bool _filesystemNeedsFlushing;
FilesystemModel* _filesystemModel;
std::deque<std::function<void()>> _filesystemQueue;
};
wxWindow* FluxEngineApp::CreateMainWindow()

View File

@@ -17,6 +17,7 @@ $(call use-library, $(OBJDIR)/tests/$1.exe, $(OBJDIR)/tests/$1.o, PROTO)
$(call use-library, $(OBJDIR)/tests/$1.exe, $(OBJDIR)/tests/$1.o, FATFS)
$(call use-library, $(OBJDIR)/tests/$1.exe, $(OBJDIR)/tests/$1.o, ADFLIB)
$(call use-library, $(OBJDIR)/tests/$1.exe, $(OBJDIR)/tests/$1.o, HFSUTILS)
$(call use-library, $(OBJDIR)/tests/$1.exe, $(OBJDIR)/tests/$1.o, LIBUSBP)
endef

View File

@@ -6,17 +6,27 @@ using namespace snowhouse;
static void testPathParsing()
{
AssertThat(Path(""), Equals(std::vector<std::string>{}));
AssertThat(Path("/"), Equals(std::vector<std::string>{}));
AssertThat(Path("one"), Equals(std::vector<std::string>{ "one" }));
AssertThat(Path("one/two"), Equals(std::vector<std::string>{ "one", "two" }));
AssertThat(Path("/one/two"), Equals(std::vector<std::string>{ "one", "two" }));
AssertThat(Path(""), Equals(std::vector<std::string>{}));
AssertThat(Path("/"), Equals(std::vector<std::string>{}));
AssertThat(Path("one"), Equals(std::vector<std::string>{"one"}));
AssertThat(Path("one/two"), Equals(std::vector<std::string>{"one", "two"}));
AssertThat(
Path("/one/two"), Equals(std::vector<std::string>{"one", "two"}));
}
static void testPathParenthood()
{
AssertThat(Path("").parent(), Equals(std::vector<std::string>{}));
AssertThat(Path("one").parent(), Equals(std::vector<std::string>{}));
AssertThat(
Path("one/two").parent(), Equals(std::vector<std::string>{"one"}));
AssertThat(Path("one/two/three").parent(),
Equals(std::vector<std::string>{"one", "two"}));
}
int main(void)
{
testPathParsing();
return 0;
testPathParsing();
testPathParenthood();
return 0;
}