mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Merge pull request #574 from davidgiven/vfs
Dramatic GUI overhaul and addition of the new file browser.
This commit is contained in:
2
Makefile
2
Makefile
@@ -41,7 +41,7 @@ AR ?= $(CCPREFIX)ar
|
|||||||
PKG_CONFIG ?= pkg-config
|
PKG_CONFIG ?= pkg-config
|
||||||
WX_CONFIG ?= wx-config
|
WX_CONFIG ?= wx-config
|
||||||
PROTOC ?= protoc
|
PROTOC ?= protoc
|
||||||
CFLAGS ?= -g -O3
|
CFLAGS ?= -g -O0
|
||||||
CXXFLAGS += -std=c++17
|
CXXFLAGS += -std=c++17
|
||||||
LDFLAGS ?=
|
LDFLAGS ?=
|
||||||
PLATFORM ?= UNIX
|
PLATFORM ?= UNIX
|
||||||
|
|||||||
11
README.md
11
README.md
@@ -4,10 +4,9 @@ FluxEngine
|
|||||||
(If you're reading this on GitHub, the formatting's a bit messed up. [Try the
|
(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/))
|
version on cowlark.com instead.](http://cowlark.com/fluxengine/))
|
||||||
|
|
||||||
**Breaking news!** As of 2021-05-21, the command line environment has changed
|
**Breaking news!** As of 2022-09-09, there's new filesystem support. Read (and
|
||||||
_substantially_ (to make it more consistent and flexible, and allow some new
|
sometimes write) files directly from (and to) your disks! It works in the GUI,
|
||||||
backend features like multi-format IBM scheme disks, which are popular with
|
too. See the details below.
|
||||||
CP/M). If things don't work the way you expect, please check the documentation.
|
|
||||||
|
|
||||||
What?
|
What?
|
||||||
-----
|
-----
|
||||||
@@ -67,6 +66,10 @@ following friendly articles:
|
|||||||
- [Configuring for your drive](doc/drives.md) ∾ but I don't have a 80 track
|
- [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
|
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
|
- [Troubleshooting dubious disks](doc/problems.md) ∾ it's not an exact
|
||||||
science ∾ the sector map ∾ clock detection and the histogram
|
science ∾ the sector map ∾ clock detection and the histogram
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ RETCODE adfRenameEntry(struct Volume *vol, SECTNUM pSect, char *oldName,
|
|||||||
BOOL intl;
|
BOOL intl;
|
||||||
RETCODE rc;
|
RETCODE rc;
|
||||||
|
|
||||||
if (strcmp(oldName,newName)==0)
|
if ((pSect == nPSect) && (strcmp(oldName,newName)==0))
|
||||||
return RC_OK;
|
return RC_OK;
|
||||||
|
|
||||||
intl = isINTL(vol->dosType) || isDIRCACHE(vol->dosType);
|
intl = isINTL(vol->dosType) || isDIRCACHE(vol->dosType);
|
||||||
|
|||||||
@@ -2350,6 +2350,28 @@ static FRESULT dir_read (
|
|||||||
/* Directory handling - Find an object in the directory */
|
/* 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 */
|
static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */
|
||||||
DIR* dp /* Pointer to the directory object with the file name */
|
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 */
|
} else { /* An SFN entry is found */
|
||||||
if (ord == 0 && sum == sum_sfn(dp->dir)) break; /* LFN matched? */
|
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 */
|
ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else /* Non LFN configuration */
|
#else /* Non LFN configuration */
|
||||||
dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK;
|
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
|
#endif
|
||||||
res = dir_next(dp, 0); /* Next entry */
|
res = dir_next(dp, 0); /* Next entry */
|
||||||
} while (res == FR_OK);
|
} while (res == FR_OK);
|
||||||
@@ -2979,9 +3001,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr
|
|||||||
sfn[i++] = d;
|
sfn[i++] = d;
|
||||||
} else { /* SBC */
|
} else { /* SBC */
|
||||||
if (strchr("*+,:;<=>[]|\"\?\x7F", (int)c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */
|
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 */
|
if (IsLower(c)) c -= 0x20; /* To upper */
|
||||||
#endif
|
|
||||||
sfn[i++] = c;
|
sfn[i++] = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,9 +162,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#define FF_ALLOW_MIXED_CASE 1
|
#define FF_CASE_INSENSITIVE_COMPARISONS 1
|
||||||
/* This option doesn't uppercase filenames before processing, in case the disk has
|
/* This option ensures that SFNs on disk are compared using case-insensitive routines,
|
||||||
* low case filenames in it. */
|
* some FAT variants store lower case filenames. dtrg */
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------/
|
/*---------------------------------------------------------------------------/
|
||||||
/ Drive/Volume Configurations
|
/ Drive/Volume Configurations
|
||||||
|
|||||||
BIN
doc/filebrowser.jpg
Normal file
BIN
doc/filebrowser.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
89
doc/filesystem.md
Normal file
89
doc/filesystem.md
Normal 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.
|
||||||
|
|
||||||
|
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "usb/usb.h"
|
#include "usb/usb.h"
|
||||||
#include "fluxsink/fluxsink.h"
|
#include "fluxsink/fluxsink.h"
|
||||||
#include "lib/fluxsink/fluxsink.pb.h"
|
#include "lib/fluxsink/fluxsink.pb.h"
|
||||||
|
#include "lib/readerwriter.cc"
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
|
|
||||||
class HardwareFluxSink : public FluxSink
|
class HardwareFluxSink : public FluxSink
|
||||||
@@ -14,32 +15,8 @@ public:
|
|||||||
HardwareFluxSink(const HardwareFluxSinkProto& conf):
|
HardwareFluxSink(const HardwareFluxSinkProto& conf):
|
||||||
_config(conf)
|
_config(conf)
|
||||||
{
|
{
|
||||||
if (config.drive().has_hard_sector_count())
|
nanoseconds_t oneRevolution;
|
||||||
{
|
measureDiskRotation(oneRevolution, _hardSectorThreshold);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~HardwareFluxSink()
|
~HardwareFluxSink()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "usb/usb.h"
|
#include "usb/usb.h"
|
||||||
#include "fluxsource/fluxsource.h"
|
#include "fluxsource/fluxsource.h"
|
||||||
#include "lib/fluxsource/fluxsource.pb.h"
|
#include "lib/fluxsource/fluxsource.pb.h"
|
||||||
|
#include "lib/readerwriter.h"
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
|
|
||||||
class HardwareFluxSource : public FluxSource
|
class HardwareFluxSource : public FluxSource
|
||||||
@@ -50,35 +51,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
HardwareFluxSource(const HardwareFluxSourceProto& conf): _config(conf)
|
HardwareFluxSource(const HardwareFluxSourceProto& conf): _config(conf)
|
||||||
{
|
{
|
||||||
int retries = 5;
|
measureDiskRotation(_oneRevolution, _hardSectorThreshold);
|
||||||
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};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~HardwareFluxSource() {}
|
~HardwareFluxSource() {}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ extern void hexdumpForSrp16(std::ostream& stream, const Bytes& bytes);
|
|||||||
|
|
||||||
struct ErrorException
|
struct ErrorException
|
||||||
{
|
{
|
||||||
|
ErrorException(const std::string& message): message(message){}
|
||||||
|
|
||||||
const std::string message;
|
const std::string message;
|
||||||
|
|
||||||
void print() const;
|
void print() const;
|
||||||
|
|||||||
123
lib/image.cc
123
lib/image.cc
@@ -1,75 +1,110 @@
|
|||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "sector.h"
|
#include "sector.h"
|
||||||
#include "image.h"
|
#include "image.h"
|
||||||
|
#include "layout.h"
|
||||||
|
|
||||||
Image::Image()
|
Image::Image() {}
|
||||||
{}
|
|
||||||
|
|
||||||
Image::Image(std::set<std::shared_ptr<const Sector>>& sectors)
|
Image::Image(std::set<std::shared_ptr<const Sector>>& sectors)
|
||||||
{
|
{
|
||||||
for (auto& sector : sectors)
|
for (auto& sector : sectors)
|
||||||
{
|
{
|
||||||
key_t key = std::make_tuple(sector->logicalTrack, sector->logicalSide, sector->logicalSector);
|
key_t key = std::make_tuple(
|
||||||
_sectors[key] = sector;
|
sector->logicalTrack, sector->logicalSide, sector->logicalSector);
|
||||||
}
|
_sectors[key] = sector;
|
||||||
calculateSize();
|
}
|
||||||
|
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
|
bool Image::empty() const
|
||||||
{
|
{
|
||||||
return _sectors.empty();
|
return _sectors.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Image::contains(unsigned track, unsigned side, unsigned sectorid) const
|
bool Image::contains(unsigned track, unsigned side, unsigned sectorid) const
|
||||||
{
|
{
|
||||||
key_t key = std::make_tuple(track, side, sectorid);
|
key_t key = std::make_tuple(track, side, sectorid);
|
||||||
return _sectors.find(key) != _sectors.end();
|
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);
|
key_t key = std::make_tuple(track, side, sectorid);
|
||||||
auto i = _sectors.find(key);
|
auto i = _sectors.find(key);
|
||||||
if (i == _sectors.end())
|
if (i == _sectors.end())
|
||||||
return NONE;
|
return NONE;
|
||||||
return i->second;
|
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);
|
key_t key = std::make_tuple(track, side, sectorid);
|
||||||
std::shared_ptr<Sector> sector = std::make_shared<Sector>();
|
std::shared_ptr<Sector> sector = std::make_shared<Sector>();
|
||||||
sector->logicalTrack = track;
|
sector->logicalTrack = track;
|
||||||
sector->logicalSide = side;
|
sector->logicalSide = side;
|
||||||
sector->logicalSector = sectorid;
|
sector->logicalSector = sectorid;
|
||||||
_sectors[key] = sector;
|
_sectors[key] = sector;
|
||||||
return sector;
|
return sector;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::erase(unsigned track, unsigned side, unsigned sectorid)
|
void Image::erase(unsigned track, unsigned side, unsigned sectorid)
|
||||||
{
|
{
|
||||||
key_t key = std::make_tuple(track, side, sectorid);
|
key_t key = std::make_tuple(track, side, sectorid);
|
||||||
_sectors.erase(key);
|
_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()
|
void Image::calculateSize()
|
||||||
{
|
{
|
||||||
_geometry = {};
|
_geometry = {};
|
||||||
unsigned maxSector = 0;
|
unsigned maxSector = 0;
|
||||||
for (const auto& i : _sectors)
|
for (const auto& i : _sectors)
|
||||||
{
|
{
|
||||||
const auto& sector = i.second;
|
const auto& sector = i.second;
|
||||||
if (sector)
|
if (sector)
|
||||||
{
|
{
|
||||||
_geometry.numTracks = std::max(_geometry.numTracks, (unsigned)sector->logicalTrack+1);
|
_geometry.numTracks = std::max(
|
||||||
_geometry.numSides = std::max(_geometry.numSides, (unsigned)sector->logicalSide+1);
|
_geometry.numTracks, (unsigned)sector->logicalTrack + 1);
|
||||||
_geometry.firstSector = std::min(_geometry.firstSector, (unsigned)sector->logicalSector);
|
_geometry.numSides =
|
||||||
maxSector = std::max(maxSector, (unsigned)sector->logicalSector);
|
std::max(_geometry.numSides, (unsigned)sector->logicalSide + 1);
|
||||||
_geometry.sectorSize = std::max(_geometry.sectorSize, (unsigned)sector->data.size());
|
_geometry.firstSector = std::min(
|
||||||
}
|
_geometry.firstSector, (unsigned)sector->logicalSector);
|
||||||
}
|
maxSector = std::max(maxSector, (unsigned)sector->logicalSector);
|
||||||
_geometry.numSectors = maxSector - _geometry.firstSector + 1;
|
_geometry.sectorSize =
|
||||||
|
std::max(_geometry.sectorSize, (unsigned)sector->data.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_geometry.numSectors = maxSector - _geometry.firstSector + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
101
lib/image.h
101
lib/image.h
@@ -3,58 +3,91 @@
|
|||||||
|
|
||||||
struct Geometry
|
struct Geometry
|
||||||
{
|
{
|
||||||
unsigned numTracks = 0;
|
unsigned numTracks = 0;
|
||||||
unsigned numSides = 0;
|
unsigned numSides = 0;
|
||||||
unsigned firstSector = UINT_MAX;
|
unsigned firstSector = UINT_MAX;
|
||||||
unsigned numSectors = 0;
|
unsigned numSectors = 0;
|
||||||
unsigned sectorSize = 0;
|
unsigned sectorSize = 0;
|
||||||
bool irregular = false;
|
bool irregular = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Image
|
class Image
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
typedef std::tuple<unsigned, unsigned, unsigned> key_t;
|
typedef std::tuple<unsigned, unsigned, unsigned> key_t;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Image();
|
Image();
|
||||||
Image(std::set<std::shared_ptr<const Sector>>& sectors);
|
Image(std::set<std::shared_ptr<const Sector>>& sectors);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class const_iterator
|
class const_iterator
|
||||||
{
|
{
|
||||||
typedef std::map<key_t, std::shared_ptr<const Sector>>::const_iterator wrapped_iterator_t;
|
typedef std::map<key_t, std::shared_ptr<const Sector>>::const_iterator
|
||||||
|
wrapped_iterator_t;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const_iterator(const wrapped_iterator_t& it): _it(it) {}
|
const_iterator(const wrapped_iterator_t& it): _it(it) {}
|
||||||
std::shared_ptr<const Sector> operator* () { return _it->second; }
|
std::shared_ptr<const Sector> operator*()
|
||||||
void operator++ () { _it++; }
|
{
|
||||||
bool operator== (const const_iterator& other) const { return _it == other._it; }
|
return _it->second;
|
||||||
bool operator!= (const const_iterator& other) const { return _it != other._it; }
|
}
|
||||||
|
|
||||||
private:
|
void operator++()
|
||||||
wrapped_iterator_t _it;
|
{
|
||||||
};
|
_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:
|
public:
|
||||||
void calculateSize();
|
void calculateSize();
|
||||||
|
|
||||||
bool empty() const;
|
void clear();
|
||||||
bool contains(unsigned track, unsigned side, unsigned sectorId) const;
|
void createBlankImage();
|
||||||
std::shared_ptr<const Sector> get(unsigned track, unsigned side, unsigned sectorId) const;
|
bool empty() const;
|
||||||
std::shared_ptr<Sector> put(unsigned track, unsigned side, unsigned sectorId);
|
bool contains(unsigned track, unsigned side, unsigned sectorId) const;
|
||||||
void erase(unsigned track, unsigned side, unsigned sectorId);
|
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()); }
|
std::set<std::pair<unsigned, unsigned>> tracks() const;
|
||||||
const_iterator end() const { return const_iterator(_sectors.cend()); }
|
|
||||||
|
|
||||||
void setGeometry(Geometry geometry) { _geometry = geometry; }
|
const_iterator begin() const
|
||||||
const Geometry& getGeometry() const { return _geometry; }
|
{
|
||||||
|
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:
|
private:
|
||||||
Geometry _geometry = {0, 0, 0};
|
Geometry _geometry = {0, 0, 0};
|
||||||
std::map<key_t, std::shared_ptr<const Sector>> _sectors;
|
std::map<key_t, std::shared_ptr<const Sector>> _sectors;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -130,46 +130,32 @@ std::set<Location> Mapper::computeLocations()
|
|||||||
{
|
{
|
||||||
std::set<Location> locations;
|
std::set<Location> locations;
|
||||||
|
|
||||||
unsigned track_step = getTrackStep();
|
|
||||||
for (unsigned logicalTrack : iterate(config.tracks()))
|
for (unsigned logicalTrack : iterate(config.tracks()))
|
||||||
{
|
{
|
||||||
for (unsigned head : iterate(config.heads()))
|
for (unsigned head : iterate(config.heads()))
|
||||||
{
|
locations.insert(computeLocationFor(logicalTrack, head));
|
||||||
unsigned physicalTrack =
|
|
||||||
config.drive().head_bias() + logicalTrack * track_step;
|
|
||||||
|
|
||||||
locations.insert({.physicalTrack = physicalTrack,
|
|
||||||
.logicalTrack = logicalTrack,
|
|
||||||
.head = head,
|
|
||||||
.groupSize = track_step});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return locations;
|
return locations;
|
||||||
}
|
}
|
||||||
|
|
||||||
Location Mapper::computeLocationFor(unsigned desiredTrack, unsigned desiredHead)
|
Location Mapper::computeLocationFor(unsigned logicalTrack, unsigned logicalHead)
|
||||||
{
|
{
|
||||||
unsigned track_step = getTrackStep();
|
if ((logicalTrack < config.layout().tracks()) &&
|
||||||
for (unsigned logicalTrack : iterate(config.tracks()))
|
(logicalHead < config.layout().sides()))
|
||||||
{
|
{
|
||||||
for (unsigned head : iterate(config.heads()))
|
unsigned track_step = getTrackStep();
|
||||||
{
|
unsigned physicalTrack =
|
||||||
if ((logicalTrack == desiredTrack) && (head == desiredHead))
|
config.drive().head_bias() + logicalTrack * track_step;
|
||||||
{
|
|
||||||
unsigned physicalTrack =
|
|
||||||
config.drive().head_bias() + logicalTrack * track_step;
|
|
||||||
|
|
||||||
return {.physicalTrack = physicalTrack,
|
return {.physicalTrack = physicalTrack,
|
||||||
.logicalTrack = logicalTrack,
|
.logicalTrack = logicalTrack,
|
||||||
.head = head,
|
.head = logicalHead,
|
||||||
.groupSize = track_step};
|
.groupSize = track_step};
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Error() << fmt::format(
|
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(
|
nanoseconds_t Mapper::calculatePhysicalClockPeriod(
|
||||||
|
|||||||
@@ -56,6 +56,46 @@ private:
|
|||||||
_cache;
|
_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
|
/* 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). */
|
* and bad version of the same sector, the bad version is dropped). */
|
||||||
|
|
||||||
@@ -185,12 +225,11 @@ ReadResult readGroup(FluxSourceIteratorHolder& fluxSourceIteratorHolder,
|
|||||||
void writeTracks(FluxSink& fluxSink,
|
void writeTracks(FluxSink& fluxSink,
|
||||||
std::function<std::unique_ptr<const Fluxmap>(const Location& location)>
|
std::function<std::unique_ptr<const Fluxmap>(const Location& location)>
|
||||||
producer,
|
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"};
|
Logger() << BeginOperationLogMessage{"Encoding and writing to disk"};
|
||||||
|
|
||||||
auto locations = Mapper::computeLocations();
|
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (const auto& location : locations)
|
for (const auto& location : locations)
|
||||||
{
|
{
|
||||||
@@ -255,8 +294,10 @@ static bool dontVerify(const Location&)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeTracks(
|
void writeTracks(FluxSink& fluxSink,
|
||||||
FluxSink& fluxSink, AbstractEncoder& encoder, const Image& image)
|
AbstractEncoder& encoder,
|
||||||
|
const Image& image,
|
||||||
|
const std::set<Location>& locations)
|
||||||
{
|
{
|
||||||
writeTracks(
|
writeTracks(
|
||||||
fluxSink,
|
fluxSink,
|
||||||
@@ -265,14 +306,16 @@ void writeTracks(
|
|||||||
auto sectors = encoder.collectSectors(location, image);
|
auto sectors = encoder.collectSectors(location, image);
|
||||||
return encoder.encode(location, sectors, image);
|
return encoder.encode(location, sectors, image);
|
||||||
},
|
},
|
||||||
dontVerify);
|
dontVerify,
|
||||||
|
locations);
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeTracksAndVerify(FluxSink& fluxSink,
|
void writeTracksAndVerify(FluxSink& fluxSink,
|
||||||
AbstractEncoder& encoder,
|
AbstractEncoder& encoder,
|
||||||
FluxSource& fluxSource,
|
FluxSource& fluxSource,
|
||||||
AbstractDecoder& decoder,
|
AbstractDecoder& decoder,
|
||||||
const Image& image)
|
const Image& image,
|
||||||
|
const std::set<Location>& locations)
|
||||||
{
|
{
|
||||||
writeTracks(
|
writeTracks(
|
||||||
fluxSink,
|
fluxSink,
|
||||||
@@ -329,14 +372,16 @@ void writeTracksAndVerify(FluxSink& fluxSink,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
},
|
||||||
|
locations);
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeDiskCommand(const Image& image,
|
void writeDiskCommand(const Image& image,
|
||||||
AbstractEncoder& encoder,
|
AbstractEncoder& encoder,
|
||||||
FluxSink& fluxSink,
|
FluxSink& fluxSink,
|
||||||
AbstractDecoder* decoder,
|
AbstractDecoder* decoder,
|
||||||
FluxSource* fluxSource)
|
FluxSource* fluxSource,
|
||||||
|
const std::set<Location>& locations)
|
||||||
{
|
{
|
||||||
const Image* imagep = ℑ
|
const Image* imagep = ℑ
|
||||||
std::unique_ptr<const Image> remapped;
|
std::unique_ptr<const Image> remapped;
|
||||||
@@ -348,9 +393,20 @@ void writeDiskCommand(const Image& image,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fluxSource && decoder)
|
if (fluxSource && decoder)
|
||||||
writeTracksAndVerify(fluxSink, encoder, *fluxSource, *decoder, *imagep);
|
writeTracksAndVerify(
|
||||||
|
fluxSink, encoder, *fluxSource, *decoder, *imagep, locations);
|
||||||
else
|
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)
|
void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink)
|
||||||
@@ -362,7 +418,8 @@ void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink)
|
|||||||
return fluxSource.readFlux(location.physicalTrack, location.head)
|
return fluxSource.readFlux(location.physicalTrack, location.head)
|
||||||
->next();
|
->next();
|
||||||
},
|
},
|
||||||
dontVerify);
|
dontVerify,
|
||||||
|
Mapper::computeLocations());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<TrackFlux> readAndDecodeTrack(
|
std::shared_ptr<TrackFlux> readAndDecodeTrack(
|
||||||
|
|||||||
@@ -14,15 +14,33 @@ class ImageWriter;
|
|||||||
class Location;
|
class Location;
|
||||||
class TrackFlux;
|
class TrackFlux;
|
||||||
|
|
||||||
|
extern void measureDiskRotation(
|
||||||
|
nanoseconds_t& oneRevolution, nanoseconds_t& hardSectorThreshold);
|
||||||
|
|
||||||
extern void writeTracks(FluxSink& fluxSink,
|
extern void writeTracks(FluxSink& fluxSink,
|
||||||
const std::function<std::unique_ptr<const Fluxmap>(
|
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,
|
extern void fillBitmapTo(std::vector<bool>& bitmap,
|
||||||
unsigned& cursor,
|
unsigned& cursor,
|
||||||
unsigned terminateAt,
|
unsigned terminateAt,
|
||||||
const std::vector<bool>& pattern);
|
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,
|
extern void writeDiskCommand(const Image& image,
|
||||||
AbstractEncoder& encoder,
|
AbstractEncoder& encoder,
|
||||||
FluxSink& fluxSink,
|
FluxSink& fluxSink,
|
||||||
@@ -31,9 +49,8 @@ extern void writeDiskCommand(const Image& image,
|
|||||||
|
|
||||||
extern void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink);
|
extern void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink);
|
||||||
|
|
||||||
extern std::shared_ptr<TrackFlux> readAndDecodeTrack(FluxSource& fluxSource,
|
extern std::shared_ptr<TrackFlux> readAndDecodeTrack(
|
||||||
AbstractDecoder& decoder,
|
FluxSource& fluxSource, AbstractDecoder& decoder, const Location& location);
|
||||||
const Location& location);
|
|
||||||
|
|
||||||
extern std::shared_ptr<const DiskFlux> readDiskCommand(
|
extern std::shared_ptr<const DiskFlux> readDiskCommand(
|
||||||
FluxSource& fluxsource, AbstractDecoder& decoder);
|
FluxSource& fluxsource, AbstractDecoder& decoder);
|
||||||
|
|||||||
@@ -74,6 +74,14 @@ bool endsWith(const std::string& value, const std::string& ending)
|
|||||||
std::equal(ending.rbegin(), ending.rend(), lowercase.begin());
|
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)
|
std::string leftTrimWhitespace(std::string value)
|
||||||
{
|
{
|
||||||
value.erase(0, value.find_first_not_of(WHITESPACE));
|
value.erase(0, value.find_first_not_of(WHITESPACE));
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ extern std::vector<std::string> split(
|
|||||||
const std::string& string, char separator);
|
const std::string& string, char separator);
|
||||||
extern bool beginsWith(const std::string& value, const std::string& beginning);
|
extern bool beginsWith(const std::string& value, const std::string& beginning);
|
||||||
extern bool endsWith(const std::string& value, const std::string& ending);
|
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 leftTrimWhitespace(std::string value);
|
||||||
extern std::string rightTrimWhitespace(std::string value);
|
extern std::string rightTrimWhitespace(std::string value);
|
||||||
extern std::string trimWhitespace(const std::string& value);
|
extern std::string trimWhitespace(const std::string& value);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public:
|
|||||||
filename = filename.substr(0, filename.find(' '));
|
filename = filename.substr(0, filename.find(' '));
|
||||||
|
|
||||||
this->inode = inode;
|
this->inode = inode;
|
||||||
|
path = {filename};
|
||||||
start_sector = ((bytes1[6] & 0x03) << 8) | bytes1[7];
|
start_sector = ((bytes1[6] & 0x03) << 8) | bytes1[7];
|
||||||
load_address =
|
load_address =
|
||||||
((bytes1[6] & 0x0c) << 14) | (bytes1[1] << 8) | bytes1[0];
|
((bytes1[6] & 0x0c) << 14) | (bytes1[1] << 8) | bytes1[0];
|
||||||
@@ -28,6 +29,18 @@ public:
|
|||||||
sector_count = (length + 255) / 256;
|
sector_count = (length + 255) / 256;
|
||||||
file_type = TYPE_FILE;
|
file_type = TYPE_FILE;
|
||||||
mode = locked ? "L" : "";
|
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:
|
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()
|
std::map<std::string, std::string> getMetadata()
|
||||||
{
|
{
|
||||||
AcornDfsDirectory dir(this);
|
AcornDfsDirectory dir(this);
|
||||||
@@ -149,26 +167,10 @@ public:
|
|||||||
return data;
|
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);
|
AcornDfsDirectory dir(this);
|
||||||
auto dirent = dir.findFile(path);
|
return 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "lib/config.pb.h"
|
#include "lib/config.pb.h"
|
||||||
#include "lib/proto.h"
|
#include "lib/proto.h"
|
||||||
#include "lib/layout.h"
|
#include "lib/layout.h"
|
||||||
|
#include "lib/logger.h"
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include "adflib.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);
|
AdfMount m(this);
|
||||||
auto* vol = m.mount();
|
auto* vol = m.mount();
|
||||||
@@ -72,7 +79,7 @@ public:
|
|||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void create(bool quick, const std::string& volumeName)
|
void create(bool quick, const std::string& volumeName) override
|
||||||
{
|
{
|
||||||
if (!quick)
|
if (!quick)
|
||||||
eraseEverythingOnDisk();
|
eraseEverythingOnDisk();
|
||||||
@@ -92,12 +99,12 @@ public:
|
|||||||
throw CannotWriteException();
|
throw CannotWriteException();
|
||||||
}
|
}
|
||||||
|
|
||||||
FilesystemStatus check()
|
FilesystemStatus check() override
|
||||||
{
|
{
|
||||||
return FS_OK;
|
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);
|
AdfMount m(this);
|
||||||
|
|
||||||
@@ -113,19 +120,13 @@ public:
|
|||||||
auto* entry = (struct Entry*)cell->content;
|
auto* entry = (struct Entry*)cell->content;
|
||||||
cell = cell->next;
|
cell = cell->next;
|
||||||
|
|
||||||
auto dirent = std::make_shared<Dirent>();
|
results.push_back(toDirent(entry, path));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> getMetadata(const Path& path)
|
std::shared_ptr<Dirent> getDirent(const Path& path) override
|
||||||
{
|
{
|
||||||
AdfMount m(this);
|
AdfMount m(this);
|
||||||
if (path.size() == 0)
|
if (path.size() == 0)
|
||||||
@@ -136,29 +137,12 @@ public:
|
|||||||
|
|
||||||
auto entry = AdfEntry(adfFindEntry(vol, (char*)path.back().c_str()));
|
auto entry = AdfEntry(adfFindEntry(vol, (char*)path.back().c_str()));
|
||||||
if (!entry)
|
if (!entry)
|
||||||
throw BadPathException();
|
throw FileNotFoundException();
|
||||||
|
|
||||||
std::map<std::string, std::string> attributes;
|
return toDirent(entry, path.parent());
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bytes getFile(const Path& path)
|
Bytes getFile(const Path& path) override
|
||||||
{
|
{
|
||||||
AdfMount m(this);
|
AdfMount m(this);
|
||||||
if (path.size() == 0)
|
if (path.size() == 0)
|
||||||
@@ -185,7 +169,7 @@ public:
|
|||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void putFile(const Path& path, const Bytes& data)
|
void putFile(const Path& path, const Bytes& data) override
|
||||||
{
|
{
|
||||||
AdfMount m(this);
|
AdfMount m(this);
|
||||||
if (path.size() == 0)
|
if (path.size() == 0)
|
||||||
@@ -209,7 +193,90 @@ public:
|
|||||||
adfCloseFile(file);
|
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:
|
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
|
class AdfEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -274,6 +341,8 @@ private:
|
|||||||
|
|
||||||
~AdfMount()
|
~AdfMount()
|
||||||
{
|
{
|
||||||
|
if (vol)
|
||||||
|
adfUnMount(vol);
|
||||||
if (self->_ffs)
|
if (self->_ffs)
|
||||||
adfUnMountDev(self->_ffs);
|
adfUnMountDev(self->_ffs);
|
||||||
adfEnvCleanUp();
|
adfEnvCleanUp();
|
||||||
@@ -282,11 +351,17 @@ private:
|
|||||||
struct Volume* mount()
|
struct Volume* mount()
|
||||||
{
|
{
|
||||||
self->_ffs = adfMountDev(nullptr, false);
|
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:
|
private:
|
||||||
AmigaFfsFilesystem* self;
|
AmigaFfsFilesystem* self;
|
||||||
|
struct Volume* vol = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
void changeDir(struct Volume* vol, const Path& path)
|
void changeDir(struct Volume* vol, const Path& path)
|
||||||
@@ -344,6 +419,16 @@ private:
|
|||||||
struct Device* _ffs;
|
struct Device* _ffs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void onAdfWarning(char* message)
|
||||||
|
{
|
||||||
|
Logger() << message;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onAdfError(char* message)
|
||||||
|
{
|
||||||
|
throw FilesystemException(message);
|
||||||
|
}
|
||||||
|
|
||||||
void adfInitNativeFct()
|
void adfInitNativeFct()
|
||||||
{
|
{
|
||||||
auto cbs = (struct nativeFunctions*)adfEnv.nativeFct;
|
auto cbs = (struct nativeFunctions*)adfEnv.nativeFct;
|
||||||
@@ -352,6 +437,9 @@ void adfInitNativeFct()
|
|||||||
cbs->adfNativeWriteSector = ::adfNativeWriteSector;
|
cbs->adfNativeWriteSector = ::adfNativeWriteSector;
|
||||||
cbs->adfIsDevNative = ::adfIsDevNative;
|
cbs->adfIsDevNative = ::adfIsDevNative;
|
||||||
cbs->adfReleaseDevice = ::adfReleaseDevice;
|
cbs->adfReleaseDevice = ::adfReleaseDevice;
|
||||||
|
|
||||||
|
adfChgEnvProp(PR_WFCT, (void*)onAdfWarning);
|
||||||
|
adfChgEnvProp(PR_EFCT, (void*)onAdfError);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RETCODE adfInitDevice(struct Device* dev, char* name, BOOL ro)
|
static RETCODE adfInitDevice(struct Device* dev, char* name, BOOL ro)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public:
|
|||||||
ByteReader br(bytes);
|
ByteReader br(bytes);
|
||||||
filename = br.read(8);
|
filename = br.read(8);
|
||||||
filename = filename.substr(0, filename.find(' '));
|
filename = filename.substr(0, filename.find(' '));
|
||||||
|
path = {filename};
|
||||||
|
|
||||||
this->inode = inode;
|
this->inode = inode;
|
||||||
brother_type = br.read_8();
|
brother_type = br.read_8();
|
||||||
@@ -40,6 +41,14 @@ public:
|
|||||||
length = sector_length * SECTOR_SIZE;
|
length = sector_length * SECTOR_SIZE;
|
||||||
file_type = TYPE_FILE;
|
file_type = TYPE_FILE;
|
||||||
mode = "";
|
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:
|
public:
|
||||||
@@ -68,7 +77,7 @@ public:
|
|||||||
|
|
||||||
auto de = std::make_unique<Brother120Dirent>(inode, buffer);
|
auto de = std::make_unique<Brother120Dirent>(inode, buffer);
|
||||||
usedSectors += de->sector_length;
|
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);
|
BrotherDirectory dir(this);
|
||||||
|
|
||||||
@@ -124,12 +138,12 @@ public:
|
|||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
FilesystemStatus check()
|
FilesystemStatus check() override
|
||||||
{
|
{
|
||||||
return FS_OK;
|
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())
|
if (!path.empty())
|
||||||
throw FileNotFoundException();
|
throw FileNotFoundException();
|
||||||
@@ -143,7 +157,7 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bytes getFile(const Path& path)
|
Bytes getFile(const Path& path) override
|
||||||
{
|
{
|
||||||
BrotherDirectory dir(this);
|
BrotherDirectory dir(this);
|
||||||
auto dirent = dir.findFile(path);
|
auto dirent = dir.findFile(path);
|
||||||
@@ -160,22 +174,10 @@ public:
|
|||||||
return data;
|
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);
|
BrotherDirectory dir(this);
|
||||||
auto dirent = dir.findFile(path);
|
return 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ class CbmfsFilesystem : public Filesystem
|
|||||||
|
|
||||||
auto filenameBytes = br.read(16).split(0xa0)[0];
|
auto filenameBytes = br.read(16).split(0xa0)[0];
|
||||||
filename = fromPetscii(filenameBytes);
|
filename = fromPetscii(filenameBytes);
|
||||||
|
path = {filename};
|
||||||
side_track = br.read_8();
|
side_track = br.read_8();
|
||||||
side_sector = br.read_8();
|
side_sector = br.read_8();
|
||||||
recordlen = br.read_8();
|
recordlen = br.read_8();
|
||||||
@@ -83,6 +84,18 @@ class CbmfsFilesystem : public Filesystem
|
|||||||
file_type = TYPE_FILE;
|
file_type = TYPE_FILE;
|
||||||
length = sectors * 254;
|
length = sectors * 254;
|
||||||
mode = "";
|
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:
|
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);
|
Directory dir(this);
|
||||||
|
|
||||||
@@ -196,12 +214,12 @@ public:
|
|||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
FilesystemStatus check()
|
FilesystemStatus check() override
|
||||||
{
|
{
|
||||||
return FS_OK;
|
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)
|
if (path.size() != 0)
|
||||||
throw BadPathException();
|
throw BadPathException();
|
||||||
@@ -214,29 +232,16 @@ public:
|
|||||||
return results;
|
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)
|
if (path.size() != 1)
|
||||||
throw BadPathException();
|
throw BadPathException();
|
||||||
Directory dir(this);
|
|
||||||
auto de = dir.findFile(unhex(path[0]));
|
|
||||||
|
|
||||||
std::map<std::string, std::string> attributes;
|
Directory dir(this);
|
||||||
attributes[FILENAME] = de->filename;
|
return dir.findFile(unhex(path[0]));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bytes getFile(const Path& path)
|
Bytes getFile(const Path& path) override
|
||||||
{
|
{
|
||||||
if (path.size() != 1)
|
if (path.size() != 1)
|
||||||
throw BadPathException();
|
throw BadPathException();
|
||||||
|
|||||||
@@ -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();
|
mount();
|
||||||
|
|
||||||
@@ -108,12 +113,12 @@ public:
|
|||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
FilesystemStatus check()
|
FilesystemStatus check() override
|
||||||
{
|
{
|
||||||
return FS_OK;
|
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();
|
mount();
|
||||||
if (!path.empty())
|
if (!path.empty())
|
||||||
@@ -130,6 +135,7 @@ public:
|
|||||||
if (!dirent)
|
if (!dirent)
|
||||||
{
|
{
|
||||||
dirent = std::make_unique<Dirent>();
|
dirent = std::make_unique<Dirent>();
|
||||||
|
dirent->path = {entry->filename};
|
||||||
dirent->filename = entry->filename;
|
dirent->filename = entry->filename;
|
||||||
dirent->mode = entry->mode;
|
dirent->mode = entry->mode;
|
||||||
dirent->length = 0;
|
dirent->length = 0;
|
||||||
@@ -142,50 +148,33 @@ public:
|
|||||||
|
|
||||||
std::vector<std::shared_ptr<Dirent>> result;
|
std::vector<std::shared_ptr<Dirent>> result;
|
||||||
for (auto& e : map)
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> getMetadata(const Path& path)
|
std::shared_ptr<Dirent> getDirent(const Path& path) override
|
||||||
{
|
{
|
||||||
mount();
|
mount();
|
||||||
if (path.size() != 1)
|
if (path.size() != 1)
|
||||||
throw BadPathException();
|
throw BadPathException();
|
||||||
|
|
||||||
std::shared_ptr<Dirent> dirent;
|
for (const auto& dirent : list(Path()))
|
||||||
for (int d = 0; d < _config.dir_entries(); d++)
|
|
||||||
{
|
{
|
||||||
auto entry = getEntry(d);
|
if (dirent->filename == path.front())
|
||||||
if (!entry)
|
return dirent;
|
||||||
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)
|
throw FileNotFoundException();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bytes getFile(const Path& path)
|
Bytes getFile(const Path& path) override
|
||||||
{
|
{
|
||||||
mount();
|
mount();
|
||||||
if (path.size() != 1)
|
if (path.size() != 1)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "lib/globals.h"
|
#include "lib/globals.h"
|
||||||
#include "lib/vfs/vfs.h"
|
#include "lib/vfs/vfs.h"
|
||||||
#include "lib/config.pb.h"
|
#include "lib/config.pb.h"
|
||||||
|
#include "lib/utils.h"
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
extern "C"
|
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();
|
mount();
|
||||||
|
|
||||||
@@ -64,31 +71,34 @@ public:
|
|||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void create(bool quick, const std::string& volumeName)
|
void create(bool quick, const std::string& volumeName) override
|
||||||
{
|
{
|
||||||
if (!quick)
|
if (!quick)
|
||||||
eraseEverythingOnDisk();
|
eraseEverythingOnDisk();
|
||||||
|
|
||||||
char buffer[FF_MAX_SS * 2];
|
char buffer[FF_MAX_SS * 2];
|
||||||
currentFatFs = this;
|
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);
|
throwError(res);
|
||||||
|
|
||||||
mount();
|
mount();
|
||||||
f_setlabel(volumeName.c_str());
|
f_setlabel(volumeName.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
FilesystemStatus check()
|
FilesystemStatus check() override
|
||||||
{
|
{
|
||||||
return FS_OK;
|
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();
|
mount();
|
||||||
|
|
||||||
DIR dir;
|
DIR dir;
|
||||||
auto pathstr = path.to_str();
|
auto pathstr = toUpper(path.to_str());
|
||||||
FRESULT res = f_opendir(&dir, pathstr.c_str());
|
FRESULT res = f_opendir(&dir, pathstr.c_str());
|
||||||
std::vector<std::shared_ptr<Dirent>> results;
|
std::vector<std::shared_ptr<Dirent>> results;
|
||||||
|
|
||||||
@@ -101,41 +111,30 @@ public:
|
|||||||
if (filinfo.fname[0] == 0)
|
if (filinfo.fname[0] == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
auto dirent = std::make_shared<Dirent>();
|
results.push_back(toDirent(filinfo, path));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f_closedir(&dir);
|
f_closedir(&dir);
|
||||||
return results;
|
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;
|
std::map<std::string, std::string> attributes;
|
||||||
|
|
||||||
mount();
|
mount();
|
||||||
auto pathstr = path.to_str();
|
auto pathstr = toUpper(path.to_str());
|
||||||
FILINFO filinfo;
|
FILINFO filinfo;
|
||||||
FRESULT res = f_stat(pathstr.c_str(), &filinfo);
|
FRESULT res = f_stat(pathstr.c_str(), &filinfo);
|
||||||
throwError(res);
|
throwError(res);
|
||||||
|
|
||||||
attributes[FILENAME] = filinfo.fname;
|
return toDirent(filinfo, path.parent());
|
||||||
attributes[LENGTH] = fmt::format("{}", filinfo.fsize);
|
|
||||||
attributes[FILE_TYPE] = (filinfo.fattrib & AM_DIR) ? "dir" : "file";
|
|
||||||
attributes[MODE] = modeToString(filinfo.fattrib);
|
|
||||||
|
|
||||||
return attributes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bytes getFile(const Path& path)
|
Bytes getFile(const Path& path) override
|
||||||
{
|
{
|
||||||
mount();
|
mount();
|
||||||
auto pathstr = path.to_str();
|
auto pathstr = toUpper(path.to_str());
|
||||||
FIL fil;
|
FIL fil;
|
||||||
FRESULT res = f_open(&fil, pathstr.c_str(), FA_READ);
|
FRESULT res = f_open(&fil, pathstr.c_str(), FA_READ);
|
||||||
throwError(res);
|
throwError(res);
|
||||||
@@ -158,10 +157,10 @@ public:
|
|||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void putFile(const Path& path, const Bytes& bytes)
|
void putFile(const Path& path, const Bytes& bytes) override
|
||||||
{
|
{
|
||||||
mount();
|
mount();
|
||||||
auto pathstr = path.to_str();
|
auto pathstr = toUpper(path.to_str());
|
||||||
FIL fil;
|
FIL fil;
|
||||||
FRESULT res =
|
FRESULT res =
|
||||||
f_open(&fil, pathstr.c_str(), FA_WRITE | FA_CREATE_ALWAYS);
|
f_open(&fil, pathstr.c_str(), FA_WRITE | FA_CREATE_ALWAYS);
|
||||||
@@ -182,6 +181,52 @@ public:
|
|||||||
f_close(&fil);
|
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:
|
public:
|
||||||
DRESULT diskRead(BYTE* buffer, LBA_t sector, UINT count)
|
DRESULT diskRead(BYTE* buffer, LBA_t sector, UINT count)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,82 +24,92 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<const Sector> get(
|
std::shared_ptr<const Sector> get(
|
||||||
unsigned track, unsigned side, unsigned sectorId)
|
unsigned track, unsigned side, unsigned sectorId) override
|
||||||
{
|
{
|
||||||
auto it = _changedSectors.get(track, side, sectorId);
|
auto it = _changedSectors.get(track, side, sectorId);
|
||||||
if (it)
|
if (it)
|
||||||
return it;
|
return it;
|
||||||
|
|
||||||
trackid_t trackid(track, side);
|
trackid_t trackid(track, side);
|
||||||
if (_loadedtracks.find(trackid) == _loadedtracks.end())
|
if (_loadedTracks.find(trackid) == _loadedTracks.end())
|
||||||
populateSectors(track, side);
|
populateSectors(track, side);
|
||||||
|
|
||||||
return _readSectors.get(track, side, sectorId);
|
return _loadedSectors.get(track, side, sectorId);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Sector> put(
|
std::shared_ptr<Sector> put(
|
||||||
unsigned track, unsigned side, unsigned sectorId)
|
unsigned track, unsigned side, unsigned sectorId) override
|
||||||
{
|
{
|
||||||
trackid_t trackid(track, side);
|
trackid_t trackid(track, side);
|
||||||
_changedtracks.insert(trackid);
|
_changedTracks.insert(trackid);
|
||||||
return _changedSectors.put(track, side, sectorId);
|
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 track = trackid.first;
|
||||||
unsigned side = trackid.second;
|
unsigned side = trackid.second;
|
||||||
auto layoutdata = Layout::getLayoutOfTrack(track, side);
|
auto layoutdata = Layout::getLayoutOfTrack(track, side);
|
||||||
auto sectors = Layout::getSectorsInTrack(layoutdata);
|
auto sectors = Layout::getSectorsInTrack(layoutdata);
|
||||||
|
locations.insert(Mapper::computeLocationFor(track, side));
|
||||||
|
|
||||||
config.mutable_tracks()->Clear();
|
/* If we don't have all the sectors of this track, we may need to
|
||||||
config.mutable_tracks()->set_start(track);
|
* populate any non-changed sectors as we can only write a track at
|
||||||
|
* a time. */
|
||||||
|
|
||||||
config.mutable_heads()->Clear();
|
if (!imageContainsAllSectorsOf(
|
||||||
config.mutable_heads()->set_start(side);
|
|
||||||
|
|
||||||
/* Check to see if we have all sectors of this track in the
|
|
||||||
* changesectors image. */
|
|
||||||
|
|
||||||
if (imageContainsAllSectorsOf(
|
|
||||||
_changedSectors, track, side, sectors))
|
_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,
|
if (_loadedTracks.find(trackid) == _loadedTracks.end())
|
||||||
*_encoder,
|
|
||||||
*_fluxSink,
|
|
||||||
&*_decoder,
|
|
||||||
&*_fluxSource);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Only a few sectors have changed. Do we need to populate the
|
|
||||||
* track? */
|
|
||||||
|
|
||||||
if (_loadedtracks.find(trackid) == _loadedtracks.end())
|
|
||||||
populateSectors(track, side);
|
populateSectors(track, side);
|
||||||
|
|
||||||
/* Now merge the loaded track with the changed one, and write
|
/* Now merge the loaded track with the changed one, and write
|
||||||
* the result back. */
|
* the result back. */
|
||||||
|
|
||||||
Image image;
|
|
||||||
for (const unsigned sector : sectors)
|
for (const unsigned sector : sectors)
|
||||||
{
|
{
|
||||||
auto s = image.put(track, side, sector);
|
if (!_changedSectors.contains(track, side, sector))
|
||||||
if (_changedSectors.contains(track, side, sector))
|
_changedSectors.put(track, side, sector)->data =
|
||||||
s->data =
|
_loadedSectors.get(track, side, sector)->data;
|
||||||
_changedSectors.get(track, side, sector)->data;
|
|
||||||
else
|
|
||||||
s->data = _readSectors.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:
|
private:
|
||||||
@@ -122,8 +132,8 @@ private:
|
|||||||
auto trackdata = readAndDecodeTrack(*_fluxSource, *_decoder, location);
|
auto trackdata = readAndDecodeTrack(*_fluxSource, *_decoder, location);
|
||||||
|
|
||||||
for (const auto& sector : trackdata->sectors)
|
for (const auto& sector : trackdata->sectors)
|
||||||
*_readSectors.put(track, side, sector->logicalSector) = *sector;
|
*_loadedSectors.put(track, side, sector->logicalSector) = *sector;
|
||||||
_loadedtracks.insert(trackid_t(track, side));
|
_loadedTracks.insert(trackid_t(track, side));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<FluxSource> _fluxSource;
|
std::shared_ptr<FluxSource> _fluxSource;
|
||||||
@@ -132,10 +142,10 @@ private:
|
|||||||
std::shared_ptr<AbstractDecoder> _decoder;
|
std::shared_ptr<AbstractDecoder> _decoder;
|
||||||
|
|
||||||
typedef std::pair<unsigned, unsigned> trackid_t;
|
typedef std::pair<unsigned, unsigned> trackid_t;
|
||||||
Image _readSectors;
|
Image _loadedSectors;
|
||||||
Image _changedSectors;
|
Image _changedSectors;
|
||||||
std::set<trackid_t> _loadedtracks;
|
std::set<trackid_t> _loadedTracks;
|
||||||
std::set<trackid_t> _changedtracks;
|
std::set<trackid_t> _changedTracks;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<SectorInterface> SectorInterface::createFluxSectorInterface(
|
std::unique_ptr<SectorInterface> SectorInterface::createFluxSectorInterface(
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
#include "lib/globals.h"
|
#include "lib/globals.h"
|
||||||
#include "lib/vfs/sectorinterface.h"
|
#include "lib/vfs/sectorinterface.h"
|
||||||
|
#include "lib/imagereader/imagereader.h"
|
||||||
|
#include "lib/imagewriter/imagewriter.h"
|
||||||
#include "lib/image.h"
|
#include "lib/image.h"
|
||||||
|
#include "lib/layout.h"
|
||||||
|
#include "lib/sector.h"
|
||||||
|
#include "lib/bytes.h"
|
||||||
|
|
||||||
class ImageSectorInterface : public SectorInterface
|
class ImageSectorInterface : public SectorInterface
|
||||||
{
|
{
|
||||||
public:
|
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:
|
public:
|
||||||
std::shared_ptr<const Sector> get(
|
std::shared_ptr<const Sector> get(
|
||||||
@@ -17,15 +28,47 @@ public:
|
|||||||
std::shared_ptr<Sector> put(
|
std::shared_ptr<Sector> put(
|
||||||
unsigned track, unsigned side, unsigned sectorId)
|
unsigned track, unsigned side, unsigned sectorId)
|
||||||
{
|
{
|
||||||
|
_changed = true;
|
||||||
return _image->put(track, side, sectorId);
|
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:
|
private:
|
||||||
std::shared_ptr<Image> _image;
|
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::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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
HfsMount m(this);
|
||||||
|
|
||||||
@@ -42,21 +48,26 @@ public:
|
|||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
FilesystemStatus check()
|
FilesystemStatus check() override
|
||||||
{
|
{
|
||||||
return FS_OK;
|
return FS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void create(bool quick, const std::string& volumeName)
|
void create(bool quick, const std::string& volumeName) override
|
||||||
{
|
{
|
||||||
if (!quick)
|
if (!quick)
|
||||||
eraseEverythingOnDisk();
|
eraseEverythingOnDisk();
|
||||||
|
|
||||||
hfs_format(
|
if (hfs_format((const char*)this,
|
||||||
(const char*)this, 0, HFS_MODE_ANY, volumeName.c_str(), 0, nullptr);
|
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);
|
HfsMount m(this);
|
||||||
|
|
||||||
@@ -64,7 +75,7 @@ public:
|
|||||||
auto pathstr = ":" + path.to_str(":");
|
auto pathstr = ":" + path.to_str(":");
|
||||||
HfsDir dir(hfs_opendir(_vol, pathstr.c_str()));
|
HfsDir dir(hfs_opendir(_vol, pathstr.c_str()));
|
||||||
if (!dir)
|
if (!dir)
|
||||||
throw FileNotFoundException();
|
throwError();
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
@@ -73,26 +84,12 @@ public:
|
|||||||
if (r != 0)
|
if (r != 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
auto dirent = std::make_shared<Dirent>();
|
results.push_back(toDirent(de, path));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> getMetadata(const Path& path)
|
std::shared_ptr<Dirent> getDirent(const Path& path) override
|
||||||
{
|
{
|
||||||
HfsMount m(this);
|
HfsMount m(this);
|
||||||
if (path.size() == 0)
|
if (path.size() == 0)
|
||||||
@@ -102,46 +99,12 @@ public:
|
|||||||
auto pathstr = ":" + path.to_str(":");
|
auto pathstr = ":" + path.to_str(":");
|
||||||
hfsdirent de;
|
hfsdirent de;
|
||||||
if (hfs_stat(_vol, pathstr.c_str(), &de))
|
if (hfs_stat(_vol, pathstr.c_str(), &de))
|
||||||
throw FileNotFoundException();
|
throwError();
|
||||||
|
|
||||||
std::map<std::string, std::string> attributes;
|
return toDirent(de, path.parent());
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bytes getFile(const Path& path)
|
Bytes getFile(const Path& path) override
|
||||||
{
|
{
|
||||||
HfsMount m(this);
|
HfsMount m(this);
|
||||||
if (path.size() == 0)
|
if (path.size() == 0)
|
||||||
@@ -151,7 +114,7 @@ public:
|
|||||||
auto pathstr = ":" + path.to_str(":");
|
auto pathstr = ":" + path.to_str(":");
|
||||||
HfsFile file(hfs_open(_vol, pathstr.c_str()));
|
HfsFile file(hfs_open(_vol, pathstr.c_str()));
|
||||||
if (!file)
|
if (!file)
|
||||||
throw FileNotFoundException();
|
throwError();
|
||||||
|
|
||||||
AppleSingle a;
|
AppleSingle a;
|
||||||
|
|
||||||
@@ -168,7 +131,7 @@ public:
|
|||||||
return a.render();
|
return a.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
void putFile(const Path& path, const Bytes& bytes)
|
void putFile(const Path& path, const Bytes& bytes) override
|
||||||
{
|
{
|
||||||
HfsMount m(this);
|
HfsMount m(this);
|
||||||
if (path.size() == 0)
|
if (path.size() == 0)
|
||||||
@@ -192,7 +155,7 @@ public:
|
|||||||
(const char*)a.type.cbegin(),
|
(const char*)a.type.cbegin(),
|
||||||
(const char*)a.creator.cbegin()));
|
(const char*)a.creator.cbegin()));
|
||||||
if (!file)
|
if (!file)
|
||||||
throw CannotWriteException();
|
throwError();
|
||||||
|
|
||||||
hfs_setfork(file, 0);
|
hfs_setfork(file, 0);
|
||||||
writeBytes(file, a.data);
|
writeBytes(file, a.data);
|
||||||
@@ -200,6 +163,146 @@ public:
|
|||||||
writeBytes(file, a.rsrc);
|
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:
|
private:
|
||||||
Bytes readBytes(hfsfile* file)
|
Bytes readBytes(hfsfile* file)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#ifndef SECTORINTERFACE_H
|
#ifndef SECTORINTERFACE_H
|
||||||
#define SECTORINTERFACE_H
|
#define SECTORINTERFACE_H
|
||||||
|
|
||||||
class Image;
|
class ImageReader;
|
||||||
|
class ImageWriter;
|
||||||
class Sector;
|
class Sector;
|
||||||
class FluxSource;
|
class FluxSource;
|
||||||
class FluxSink;
|
class FluxSink;
|
||||||
@@ -16,11 +17,24 @@ public:
|
|||||||
virtual std::shared_ptr<Sector> put(
|
virtual std::shared_ptr<Sector> put(
|
||||||
unsigned track, unsigned side, unsigned sectorId) = 0;
|
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:
|
public:
|
||||||
static std::unique_ptr<SectorInterface> createImageSectorInterface(
|
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(
|
static std::unique_ptr<SectorInterface> createFluxSectorInterface(
|
||||||
std::shared_ptr<FluxSource> fluxSource,
|
std::shared_ptr<FluxSource> fluxSource,
|
||||||
std::shared_ptr<FluxSink> fluxSink,
|
std::shared_ptr<FluxSink> fluxSink,
|
||||||
|
|||||||
101
lib/vfs/vfs.cc
101
lib/vfs/vfs.cc
@@ -6,9 +6,20 @@
|
|||||||
#include "lib/image.h"
|
#include "lib/image.h"
|
||||||
#include "lib/sector.h"
|
#include "lib/sector.h"
|
||||||
#include "lib/vfs/sectorinterface.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/config.pb.h"
|
||||||
#include "lib/utils.h"
|
#include "lib/utils.h"
|
||||||
|
|
||||||
|
Path::Path(const std::vector<std::string> other):
|
||||||
|
std::vector<std::string>(other)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Path::Path(const std::string& path)
|
Path::Path(const std::string& path)
|
||||||
{
|
{
|
||||||
if (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
|
std::string Path::to_str(const std::string sep) const
|
||||||
{
|
{
|
||||||
return join(*this, sep);
|
return join(*this, sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t Filesystem::capabilities() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void Filesystem::create(bool quick, const std::string& volumeName)
|
void Filesystem::create(bool quick, const std::string& volumeName)
|
||||||
{
|
{
|
||||||
throw UnimplementedFilesystemException();
|
throw UnimplementedFilesystemException();
|
||||||
@@ -68,7 +102,7 @@ void Filesystem::putFile(const Path& path, const Bytes& data)
|
|||||||
throw UnimplementedFilesystemException();
|
throw UnimplementedFilesystemException();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> Filesystem::getMetadata(const Path& path)
|
std::shared_ptr<Dirent> Filesystem::getDirent(const Path& path)
|
||||||
{
|
{
|
||||||
throw UnimplementedFilesystemException();
|
throw UnimplementedFilesystemException();
|
||||||
}
|
}
|
||||||
@@ -89,9 +123,29 @@ void Filesystem::deleteFile(const Path& path)
|
|||||||
throw UnimplementedFilesystemException();
|
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):
|
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)
|
Bytes Filesystem::getSector(unsigned track, unsigned side, unsigned sector)
|
||||||
{
|
{
|
||||||
auto s = _sectors->get(track, side, sector);
|
auto s = _sectors->get(track, side, sector);
|
||||||
|
|||||||
@@ -10,18 +10,34 @@ class DfsProto;
|
|||||||
class FilesystemProto;
|
class FilesystemProto;
|
||||||
class SectorInterface;
|
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
|
enum FileType
|
||||||
{
|
{
|
||||||
|
TYPE__INVALID,
|
||||||
TYPE_FILE,
|
TYPE_FILE,
|
||||||
TYPE_DIRECTORY
|
TYPE_DIRECTORY
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Dirent
|
struct Dirent
|
||||||
{
|
{
|
||||||
|
Path path;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
FileType file_type;
|
FileType file_type;
|
||||||
uint32_t length;
|
uint32_t length;
|
||||||
std::string mode;
|
std::string mode;
|
||||||
|
std::map<std::string, std::string> attributes;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum FilesystemStatus
|
enum FilesystemStatus
|
||||||
@@ -33,37 +49,50 @@ enum FilesystemStatus
|
|||||||
FS_BAD
|
FS_BAD
|
||||||
};
|
};
|
||||||
|
|
||||||
class FilesystemException
|
class FilesystemException : public ErrorException
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FilesystemException(const std::string& message): message(message) {}
|
FilesystemException(const std::string& message): ErrorException(message) {}
|
||||||
|
|
||||||
public:
|
|
||||||
std::string message;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class BadPathException : public FilesystemException
|
class BadPathException : public FilesystemException
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BadPathException(): FilesystemException("Bad path") {}
|
BadPathException(): FilesystemException("Bad path") {}
|
||||||
|
|
||||||
|
BadPathException(const std::string& msg): FilesystemException(msg) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileNotFoundException : public FilesystemException
|
class FileNotFoundException : public FilesystemException
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FileNotFoundException(): FilesystemException("File not found") {}
|
FileNotFoundException(): FilesystemException("File not found") {}
|
||||||
|
|
||||||
|
FileNotFoundException(const std::string& msg): FilesystemException(msg) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class BadFilesystemException : public FilesystemException
|
class BadFilesystemException : public FilesystemException
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BadFilesystemException(): FilesystemException("Invalid filesystem") {}
|
BadFilesystemException(): FilesystemException("Invalid filesystem") {}
|
||||||
|
|
||||||
|
BadFilesystemException(const std::string& msg): FilesystemException(msg) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CannotWriteException : public FilesystemException
|
class CannotWriteException : public FilesystemException
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CannotWriteException(): FilesystemException("Cannot write file") {}
|
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
|
class ReadOnlyFilesystemException : public FilesystemException
|
||||||
@@ -72,6 +101,11 @@ public:
|
|||||||
ReadOnlyFilesystemException(): FilesystemException("Read only filesystem")
|
ReadOnlyFilesystemException(): FilesystemException("Read only filesystem")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReadOnlyFilesystemException(const std::string& msg):
|
||||||
|
FilesystemException(msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class UnimplementedFilesystemException : public FilesystemException
|
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
|
class Filesystem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -111,9 +135,28 @@ public:
|
|||||||
static constexpr const char* USED_BLOCKS = "used_blocks";
|
static constexpr const char* USED_BLOCKS = "used_blocks";
|
||||||
static constexpr const char* BLOCK_SIZE = "block_size";
|
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:
|
public:
|
||||||
|
/* Retrieve capability information. */
|
||||||
|
virtual uint32_t capabilities() const;
|
||||||
|
|
||||||
/* Create a filesystem on the disk. */
|
/* 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
|
/* Are all sectors on the filesystem present and good? (Does not check
|
||||||
* filesystem consistency.) */
|
* filesystem consistency.) */
|
||||||
@@ -135,8 +178,8 @@ public:
|
|||||||
/* Write a file. */
|
/* Write a file. */
|
||||||
virtual void putFile(const Path& path, const Bytes& data);
|
virtual void putFile(const Path& path, const Bytes& data);
|
||||||
|
|
||||||
/* Get file metadata. */
|
/* Get a single file dirent. */
|
||||||
virtual std::map<std::string, std::string> getMetadata(const Path& path);
|
virtual std::shared_ptr<Dirent> getDirent(const Path& path);
|
||||||
|
|
||||||
/* Update file metadata. */
|
/* Update file metadata. */
|
||||||
virtual void putMetadata(
|
virtual void putMetadata(
|
||||||
@@ -148,8 +191,20 @@ public:
|
|||||||
/* Deletes a file or non-empty directory. */
|
/* Deletes a file or non-empty directory. */
|
||||||
virtual void deleteFile(const Path& path);
|
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. */
|
/* Flushes any changes back to the disk. */
|
||||||
void flush();
|
void flushChanges();
|
||||||
|
|
||||||
|
/* Discards any pending changes. */
|
||||||
|
void discardChanges();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Filesystem(std::shared_ptr<SectorInterface> sectors);
|
Filesystem(std::shared_ptr<SectorInterface> sectors);
|
||||||
@@ -188,6 +243,7 @@ public:
|
|||||||
|
|
||||||
static std::unique_ptr<Filesystem> createFilesystem(
|
static std::unique_ptr<Filesystem> createFilesystem(
|
||||||
const FilesystemProto& config, std::shared_ptr<SectorInterface> image);
|
const FilesystemProto& config, std::shared_ptr<SectorInterface> image);
|
||||||
|
static std::unique_ptr<Filesystem> createFilesystemFromConfig();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ FLUXENGINE_SRCS = \
|
|||||||
src/fe-getfileinfo.cc \
|
src/fe-getfileinfo.cc \
|
||||||
src/fe-inspect.cc \
|
src/fe-inspect.cc \
|
||||||
src/fe-ls.cc \
|
src/fe-ls.cc \
|
||||||
|
src/fe-mkdir.cc \
|
||||||
|
src/fe-mv.cc \
|
||||||
|
src/fe-rm.cc \
|
||||||
src/fe-putfile.cc \
|
src/fe-putfile.cc \
|
||||||
src/fe-rawread.cc \
|
src/fe-rawread.cc \
|
||||||
src/fe-rawwrite.cc \
|
src/fe-rawwrite.cc \
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ int mainFormat(int argc, const char* argv[])
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto filesystem = createFilesystemFromConfig();
|
auto filesystem = Filesystem::createFilesystemFromConfig();
|
||||||
filesystem->create(quick, volumeName);
|
filesystem->create(quick, volumeName);
|
||||||
filesystem->flush();
|
filesystem->flushChanges();
|
||||||
}
|
}
|
||||||
catch (const FilesystemException& e)
|
catch (const FilesystemException& e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ int mainGetDiskInfo(int argc, const char* argv[])
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto filesystem = createFilesystemFromConfig();
|
auto filesystem = Filesystem::createFilesystemFromConfig();
|
||||||
auto attributes = filesystem->getMetadata();
|
auto attributes = filesystem->getMetadata();
|
||||||
|
|
||||||
for (const auto& e : attributes)
|
for (const auto& e : attributes)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ int mainGetFile(int argc, const char* argv[])
|
|||||||
if (outputFilename.empty())
|
if (outputFilename.empty())
|
||||||
outputFilename = inputFilename.back();
|
outputFilename = inputFilename.back();
|
||||||
|
|
||||||
auto filesystem = createFilesystemFromConfig();
|
auto filesystem = Filesystem::createFilesystemFromConfig();
|
||||||
auto data = filesystem->getFile(inputFilename);
|
auto data = filesystem->getFile(inputFilename);
|
||||||
data.writeToFile(outputFilename);
|
data.writeToFile(outputFilename);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ int mainGetFileInfo(int argc, const char* argv[])
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto filesystem = createFilesystemFromConfig();
|
auto filesystem = Filesystem::createFilesystemFromConfig();
|
||||||
auto attributes = filesystem->getMetadata(Path(directory));
|
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));
|
fmt::print("{}={}\n", e.first, quote(e.second));
|
||||||
}
|
}
|
||||||
catch (const FilesystemException& e)
|
catch (const FilesystemException& e)
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ int mainLs(int argc, const char* argv[])
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto filesystem = createFilesystemFromConfig();
|
auto filesystem = Filesystem::createFilesystemFromConfig();
|
||||||
auto files = filesystem->list(Path(directory));
|
auto files = filesystem->list(Path(directory));
|
||||||
|
|
||||||
int maxlen = 0;
|
int maxlen = 0;
|
||||||
|
|||||||
39
src/fe-mkdir.cc
Normal file
39
src/fe-mkdir.cc
Normal 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
44
src/fe-mv.cc
Normal 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;
|
||||||
|
}
|
||||||
@@ -38,9 +38,9 @@ int mainPutFile(int argc, const char* argv[])
|
|||||||
Error() << "you must supply a destination path to write to";
|
Error() << "you must supply a destination path to write to";
|
||||||
|
|
||||||
auto data = Bytes::readFromFile(inputFilename);
|
auto data = Bytes::readFromFile(inputFilename);
|
||||||
auto filesystem = createFilesystemFromConfig();
|
auto filesystem = Filesystem::createFilesystemFromConfig();
|
||||||
filesystem->putFile(outputFilename, data);
|
filesystem->putFile(outputFilename, data);
|
||||||
filesystem->flush();
|
filesystem->flushChanges();
|
||||||
}
|
}
|
||||||
catch (const FilesystemException& e)
|
catch (const FilesystemException& e)
|
||||||
{
|
{
|
||||||
|
|||||||
39
src/fe-rm.cc
Normal file
39
src/fe-rm.cc
Normal 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;
|
||||||
|
}
|
||||||
@@ -4,11 +4,10 @@
|
|||||||
#include "sector.h"
|
#include "sector.h"
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
#include "readerwriter.h"
|
#include "readerwriter.h"
|
||||||
#include "lib/decoders/decoders.h"
|
|
||||||
#include "lib/encoders/encoders.h"
|
|
||||||
#include "lib/fluxsource/fluxsource.h"
|
#include "lib/fluxsource/fluxsource.h"
|
||||||
#include "lib/fluxsink/fluxsink.h"
|
#include "lib/fluxsink/fluxsink.h"
|
||||||
#include "lib/imagereader/imagereader.h"
|
#include "lib/imagereader/imagereader.h"
|
||||||
|
#include "lib/imagewriter/imagewriter.h"
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
#include "fluxengine.h"
|
#include "fluxengine.h"
|
||||||
#include "lib/vfs/sectorinterface.h"
|
#include "lib/vfs/sectorinterface.h"
|
||||||
@@ -25,6 +24,8 @@ static StringFlag image({"-i", "--image"},
|
|||||||
{
|
{
|
||||||
ImageReader::updateConfigForFilename(
|
ImageReader::updateConfigForFilename(
|
||||||
config.mutable_image_reader(), value);
|
config.mutable_image_reader(), value);
|
||||||
|
ImageWriter::updateConfigForFilename(
|
||||||
|
config.mutable_image_writer(), value);
|
||||||
});
|
});
|
||||||
|
|
||||||
static StringFlag flux({"-f", "--flux"},
|
static StringFlag flux({"-f", "--flux"},
|
||||||
@@ -38,24 +39,3 @@ static StringFlag flux({"-f", "--flux"},
|
|||||||
config.mutable_flux_sink(), value);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,5 @@
|
|||||||
|
|
||||||
extern FlagGroup fileFlags;
|
extern FlagGroup fileFlags;
|
||||||
|
|
||||||
extern std::unique_ptr<Filesystem> createFilesystemFromConfig();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,13 @@ extern command_cb mainGetFile;
|
|||||||
extern command_cb mainGetFileInfo;
|
extern command_cb mainGetFileInfo;
|
||||||
extern command_cb mainInspect;
|
extern command_cb mainInspect;
|
||||||
extern command_cb mainLs;
|
extern command_cb mainLs;
|
||||||
|
extern command_cb mainMkDir;
|
||||||
|
extern command_cb mainMv;
|
||||||
extern command_cb mainPutFile;
|
extern command_cb mainPutFile;
|
||||||
extern command_cb mainRawRead;
|
extern command_cb mainRawRead;
|
||||||
extern command_cb mainRawWrite;
|
extern command_cb mainRawWrite;
|
||||||
extern command_cb mainRead;
|
extern command_cb mainRead;
|
||||||
|
extern command_cb mainRm;
|
||||||
extern command_cb mainRpm;
|
extern command_cb mainRpm;
|
||||||
extern command_cb mainSeek;
|
extern command_cb mainSeek;
|
||||||
extern command_cb mainTestBandwidth;
|
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.", },
|
{ "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).", },
|
{ "getdiskinfo", mainGetDiskInfo, "Read volume metadata off a disk (or image).", },
|
||||||
{ "ls", mainLs, "Show files on 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).", },
|
{ "getfile", mainGetFile, "Read a file off a disk (or image).", },
|
||||||
{ "getfileinfo", mainGetFileInfo, "Read file metadata off a disk (or image).", },
|
{ "getfileinfo", mainGetFileInfo, "Read file metadata off a disk (or image).", },
|
||||||
{ "putfile", mainPutFile, "Write a file to disk (or image).", },
|
{ "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.", },
|
{ "rpm", mainRpm, "Measures the disk rotational speed.", },
|
||||||
{ "seek", mainSeek, "Moves the disk head.", },
|
{ "seek", mainSeek, "Moves the disk head.", },
|
||||||
{ "test", mainTest, "Various testing commands.", },
|
{ "test", mainTest, "Various testing commands.", },
|
||||||
@@ -133,7 +139,7 @@ void showProfiles(const std::string& command, const std::map<std::string, std::s
|
|||||||
ConfigProto config;
|
ConfigProto config;
|
||||||
if (!config.ParseFromString(it.second))
|
if (!config.ParseFromString(it.second))
|
||||||
Error() << "couldn't load config proto";
|
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());
|
std::cout << fmt::format(" {}: {}\n", it.first, config.comment());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
src/formats/_atari.textpb
Normal file
3
src/formats/_atari.textpb
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
comment: 'Common Atari definitions'
|
||||||
|
is_extension: true
|
||||||
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
comment: 'Atari ST 360kB 3.5" 80-track 9-sector SSDD'
|
comment: 'Atari ST 360kB 3.5" 80-track 9-sector SSDD'
|
||||||
|
|
||||||
|
include: '_atari'
|
||||||
|
|
||||||
image_reader {
|
image_reader {
|
||||||
filename: "atarist360.st"
|
filename: "atarist360.st"
|
||||||
img {}
|
img {}
|
||||||
@@ -53,7 +55,3 @@ heads {
|
|||||||
end: 0
|
end: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
filesystem {
|
|
||||||
fatfs {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
comment: 'Atari ST 370kB 3.5" 82-track 9-sector SSDD'
|
comment: 'Atari ST 370kB 3.5" 82-track 9-sector SSDD'
|
||||||
|
|
||||||
|
include: '_atari'
|
||||||
|
|
||||||
image_reader {
|
image_reader {
|
||||||
filename: "atarist370.st"
|
filename: "atarist370.st"
|
||||||
img {}
|
img {}
|
||||||
@@ -53,8 +55,4 @@ heads {
|
|||||||
end: 0
|
end: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
filesystem {
|
|
||||||
fatfs {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
comment: 'Atari ST 400kB 3.5" 80-track 10-sector SSDD'
|
comment: 'Atari ST 400kB 3.5" 80-track 10-sector SSDD'
|
||||||
|
|
||||||
|
include: '_atari'
|
||||||
|
|
||||||
image_reader {
|
image_reader {
|
||||||
filename: "atarist400.st"
|
filename: "atarist400.st"
|
||||||
img {}
|
img {}
|
||||||
@@ -53,8 +55,4 @@ heads {
|
|||||||
end: 0
|
end: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
filesystem {
|
|
||||||
fatfs {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
comment: 'Atari ST 410kB 3.5" 82-track 10-sector SSDD'
|
comment: 'Atari ST 410kB 3.5" 82-track 10-sector SSDD'
|
||||||
|
|
||||||
|
include: '_atari'
|
||||||
|
|
||||||
image_reader {
|
image_reader {
|
||||||
filename: "atarist410.st"
|
filename: "atarist410.st"
|
||||||
img {}
|
img {}
|
||||||
@@ -53,8 +55,4 @@ heads {
|
|||||||
end: 0
|
end: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
filesystem {
|
|
||||||
fatfs {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
comment: 'Atari ST 720kB 3.5" 80-track 9-sector DSDD'
|
comment: 'Atari ST 720kB 3.5" 80-track 9-sector DSDD'
|
||||||
|
|
||||||
|
include: '_atari'
|
||||||
|
|
||||||
image_reader {
|
image_reader {
|
||||||
filename: "atarist720.st"
|
filename: "atarist720.st"
|
||||||
img {}
|
img {}
|
||||||
@@ -53,8 +55,4 @@ heads {
|
|||||||
end: 1
|
end: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
filesystem {
|
|
||||||
fatfs {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
comment: 'Atari ST 740kB 3.5" 82-track 9-sector DSDD'
|
comment: 'Atari ST 740kB 3.5" 82-track 9-sector DSDD'
|
||||||
|
|
||||||
|
include: '_atari'
|
||||||
|
|
||||||
image_reader {
|
image_reader {
|
||||||
filename: "atarist740.st"
|
filename: "atarist740.st"
|
||||||
img {}
|
img {}
|
||||||
@@ -53,8 +55,4 @@ heads {
|
|||||||
end: 1
|
end: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
filesystem {
|
|
||||||
fatfs {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
comment: 'Atari ST 800kB 3.5" 80-track 10-sector DSDD'
|
comment: 'Atari ST 800kB 3.5" 80-track 10-sector DSDD'
|
||||||
|
|
||||||
|
include: '_atari'
|
||||||
|
|
||||||
image_reader {
|
image_reader {
|
||||||
filename: "atarist800.st"
|
filename: "atarist800.st"
|
||||||
img {}
|
img {}
|
||||||
@@ -53,8 +55,4 @@ heads {
|
|||||||
end: 1
|
end: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
filesystem {
|
|
||||||
fatfs {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
comment: 'Atari ST 820kB 3.5" 82-track 10-sector DSDD'
|
comment: 'Atari ST 820kB 3.5" 82-track 10-sector DSDD'
|
||||||
|
|
||||||
|
include: '_atari'
|
||||||
|
|
||||||
image_reader {
|
image_reader {
|
||||||
filename: "atarist820.st"
|
filename: "atarist820.st"
|
||||||
img {}
|
img {}
|
||||||
@@ -53,8 +55,4 @@ heads {
|
|||||||
end: 1
|
end: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
filesystem {
|
|
||||||
fatfs {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
FORMATS = \
|
FORMATS = \
|
||||||
|
_atari \
|
||||||
_micropolis \
|
_micropolis \
|
||||||
_northstar \
|
_northstar \
|
||||||
_mx \
|
_mx \
|
||||||
@@ -72,7 +73,7 @@ $(OBJDIR)/src/formats/format_%.cc: $(OBJDIR)/protoencode_ConfigProto.exe src/for
|
|||||||
|
|
||||||
OBJS += $(patsubst %, $(OBJDIR)/src/formats/format_%.o, $(FORMATS))
|
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 $@)
|
@mkdir -p $(dir $@)
|
||||||
@echo MKTABLE $@
|
@echo MKTABLE $@
|
||||||
@scripts/mktable.sh formats $(FORMATS) > $@
|
@scripts/mktable.sh formats $(FORMATS) > $@
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ ifneq ($(shell $(WX_CONFIG) --version),)
|
|||||||
|
|
||||||
FLUXENGINE_GUI_SRCS = \
|
FLUXENGINE_GUI_SRCS = \
|
||||||
src/gui/customstatusbar.cc \
|
src/gui/customstatusbar.cc \
|
||||||
|
src/gui/filesystemmodel.cc \
|
||||||
src/gui/fluxviewercontrol.cc \
|
src/gui/fluxviewercontrol.cc \
|
||||||
src/gui/fluxviewerwindow.cc \
|
src/gui/fluxviewerwindow.cc \
|
||||||
src/gui/layout.cpp \
|
src/gui/layout.cpp \
|
||||||
@@ -9,6 +10,7 @@ FLUXENGINE_GUI_SRCS = \
|
|||||||
src/gui/mainwindow.cc \
|
src/gui/mainwindow.cc \
|
||||||
src/gui/texteditorwindow.cc \
|
src/gui/texteditorwindow.cc \
|
||||||
src/gui/textviewerwindow.cc \
|
src/gui/textviewerwindow.cc \
|
||||||
|
src/gui/fileviewerwindow.cc \
|
||||||
src/gui/visualisationcontrol.cc \
|
src/gui/visualisationcontrol.cc \
|
||||||
|
|
||||||
FLUXENGINE_GUI_OBJS = \
|
FLUXENGINE_GUI_OBJS = \
|
||||||
@@ -17,9 +19,9 @@ FLUXENGINE_GUI_OBJS = \
|
|||||||
)
|
)
|
||||||
OBJS += $(FLUXENGINE_GUI_OBJS)
|
OBJS += $(FLUXENGINE_GUI_OBJS)
|
||||||
$(FLUXENGINE_GUI_SRCS): | $(PROTO_HDRS)
|
$(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 = $(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)
|
$(FLUXENGINE_GUI_BIN): $(FLUXENGINE_GUI_OBJS)
|
||||||
|
|
||||||
$(call use-pkgconfig, $(FLUXENGINE_GUI_BIN), $(FLUXENGINE_GUI_OBJS), fmt)
|
$(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), LIBFORMATS)
|
||||||
$(call use-library, $(FLUXENGINE_GUI_BIN), $(FLUXENGINE_GUI_OBJS), LIBUSBP)
|
$(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), 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)
|
binaries: fluxengine-gui$(EXT)
|
||||||
|
|
||||||
|
|||||||
299
src/gui/filesystemmodel.cc
Normal file
299
src/gui/filesystemmodel.cc
Normal 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
38
src/gui/filesystemmodel.h
Normal 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
|
||||||
50
src/gui/fileviewerwindow.cc
Normal file
50
src/gui/fileviewerwindow.cc
Normal 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();
|
||||||
|
}
|
||||||
16
src/gui/fileviewerwindow.h
Normal file
16
src/gui/fileviewerwindow.h
Normal 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
|
||||||
@@ -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/
|
// http://www.wxformbuilder.org/
|
||||||
//
|
//
|
||||||
// PLEASE DO *NOT* EDIT THIS FILE!
|
// 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 = new wxButton( idlePanel, wxID_ANY, wxT("Browse disk"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||||
|
|
||||||
browseButton->SetBitmap( wxArtProvider::GetBitmap( wxART_FOLDER_OPEN, wxART_TOOLBAR ) );
|
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.") );
|
browseButton->SetToolTip( wxT("Access the files on the disk directly without needing to image it.") );
|
||||||
|
|
||||||
gSizer9->Add( browseButton, 0, wxALL|wxEXPAND, 5 );
|
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 );
|
fgSizer8->Add( gSizer9, 1, wxEXPAND, 5 );
|
||||||
|
|
||||||
@@ -206,7 +210,7 @@ MainWindowGen::MainWindowGen( wxWindow* parent, wxWindowID id, const wxString& t
|
|||||||
wxBoxSizer* bSizer41;
|
wxBoxSizer* bSizer41;
|
||||||
bSizer41 = new wxBoxSizer( wxVERTICAL );
|
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 );
|
imagerBackTool = imagerToolbar->AddTool( wxID_ANY, wxT("Back"), wxArtProvider::GetBitmap( wxART_GO_BACK, wxART_TOOLBAR ), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString, NULL );
|
||||||
|
|
||||||
imagerToolbar->Realize();
|
imagerToolbar->Realize();
|
||||||
@@ -272,30 +276,68 @@ MainWindowGen::MainWindowGen( wxWindow* parent, wxWindowID id, const wxString& t
|
|||||||
fgSizer23->SetFlexibleDirection( wxBOTH );
|
fgSizer23->SetFlexibleDirection( wxBOTH );
|
||||||
fgSizer23->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
|
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 );
|
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();
|
browserToolbar->Realize();
|
||||||
|
|
||||||
fgSizer23->Add( browserToolbar, 0, wxEXPAND, 5 );
|
fgSizer23->Add( browserToolbar, 0, wxEXPAND, 5 );
|
||||||
|
|
||||||
m_scrolledWindow1 = new wxScrolledWindow( browsePanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL );
|
browserTree = new wxDataViewCtrl( browsePanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_SINGLE );
|
||||||
m_scrolledWindow1->SetScrollRate( 5, 5 );
|
m_dataViewColumn1 = browserTree->AppendIconTextColumn( wxT("Filename"), 0, wxDATAVIEW_CELL_EDITABLE, 250, static_cast<wxAlignment>(wxALIGN_LEFT), wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE );
|
||||||
wxGridSizer* gSizer13;
|
m_dataViewColumn2 = browserTree->AppendTextColumn( wxT("Size"), 1, wxDATAVIEW_CELL_INERT, 100, static_cast<wxAlignment>(wxALIGN_RIGHT), wxDATAVIEW_COL_RESIZABLE );
|
||||||
gSizer13 = new wxGridSizer( 1, 1, 0, 0 );
|
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 );
|
diskSpaceGauge = new wxGauge( browsePanel, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL|wxGA_SMOOTH );
|
||||||
browserFilenameColumn = browserView->AppendTextColumn( wxT("Filename"), 0, wxDATAVIEW_CELL_INERT, -1, static_cast<wxAlignment>(wxALIGN_LEFT), wxDATAVIEW_COL_RESIZABLE );
|
diskSpaceGauge->SetValue( 0 );
|
||||||
browserModeColumn = browserView->AppendTextColumn( wxT("Mode"), 1, wxDATAVIEW_CELL_INERT, -1, static_cast<wxAlignment>(wxALIGN_LEFT), wxDATAVIEW_COL_RESIZABLE );
|
fgSizer23->Add( diskSpaceGauge, 0, wxALL|wxEXPAND, 5 );
|
||||||
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 );
|
|
||||||
|
|
||||||
wxGridSizer* gSizer12;
|
wxGridSizer* gSizer12;
|
||||||
gSizer12 = new wxGridSizer( 0, 2, 0, 0 );
|
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 );
|
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->SetSizer( fgSizer23 );
|
||||||
browsePanel->Layout();
|
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 );
|
customConfigurationButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnCustomConfigurationButton ), NULL, this );
|
||||||
readButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnReadButton ), NULL, this );
|
readButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnReadButton ), NULL, this );
|
||||||
writeButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnWriteButton ), 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 ) );
|
this->Connect( imagerBackTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBackButton ) );
|
||||||
imagerSaveImageButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnSaveImageButton ), NULL, this );
|
imagerSaveImageButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnSaveImageButton ), NULL, this );
|
||||||
imagerSaveFluxButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnSaveFluxButton ), NULL, this );
|
imagerSaveFluxButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnSaveFluxButton ), NULL, this );
|
||||||
imagerGoAgainButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnImagerGoAgainButton ), 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( 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()
|
MainWindowGen::~MainWindowGen()
|
||||||
@@ -368,12 +432,27 @@ MainWindowGen::~MainWindowGen()
|
|||||||
customConfigurationButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnCustomConfigurationButton ), NULL, this );
|
customConfigurationButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnCustomConfigurationButton ), NULL, this );
|
||||||
readButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnReadButton ), NULL, this );
|
readButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnReadButton ), NULL, this );
|
||||||
writeButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnWriteButton ), 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 ) );
|
this->Disconnect( imagerBackTool->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( MainWindowGen::OnBackButton ) );
|
||||||
imagerSaveImageButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnSaveImageButton ), NULL, this );
|
imagerSaveImageButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnSaveImageButton ), NULL, this );
|
||||||
imagerSaveFluxButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnSaveFluxButton ), NULL, this );
|
imagerSaveFluxButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnSaveFluxButton ), NULL, this );
|
||||||
imagerGoAgainButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainWindowGen::OnImagerGoAgainButton ), 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( 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 )
|
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 );
|
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()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
2604
src/gui/layout.fbp
2604
src/gui/layout.fbp
File diff suppressed because it is too large
Load Diff
227
src/gui/layout.h
227
src/gui/layout.h
@@ -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/
|
// http://www.wxformbuilder.org/
|
||||||
//
|
//
|
||||||
// PLEASE DO *NOT* EDIT THIS FILE!
|
// PLEASE DO *NOT* EDIT THIS FILE!
|
||||||
@@ -28,15 +28,19 @@
|
|||||||
#include <wx/filepicker.h>
|
#include <wx/filepicker.h>
|
||||||
#include <wx/button.h>
|
#include <wx/button.h>
|
||||||
#include <wx/scrolwin.h>
|
#include <wx/scrolwin.h>
|
||||||
#include <wx/toolbar.h>
|
#include <wx/aui/aui.h>
|
||||||
|
#include <wx/aui/auibar.h>
|
||||||
#include "visualisationcontrol.h"
|
#include "visualisationcontrol.h"
|
||||||
#include <wx/dataview.h>
|
#include <wx/dataview.h>
|
||||||
|
#include <wx/gauge.h>
|
||||||
#include <wx/simplebook.h>
|
#include <wx/simplebook.h>
|
||||||
#include <wx/frame.h>
|
#include <wx/frame.h>
|
||||||
#include <wx/textctrl.h>
|
#include <wx/textctrl.h>
|
||||||
#include <wx/dialog.h>
|
#include <wx/dialog.h>
|
||||||
#include "fluxviewercontrol.h"
|
#include "fluxviewercontrol.h"
|
||||||
#include <wx/scrolbar.h>
|
#include <wx/scrolbar.h>
|
||||||
|
#include <wx/notebook.h>
|
||||||
|
#include <wx/gbsizer.h>
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -73,25 +77,36 @@ class MainWindowGen : public wxFrame
|
|||||||
wxButton* readButton;
|
wxButton* readButton;
|
||||||
wxButton* writeButton;
|
wxButton* writeButton;
|
||||||
wxButton* browseButton;
|
wxButton* browseButton;
|
||||||
|
wxButton* formatButton;
|
||||||
wxPanel* imagePanel;
|
wxPanel* imagePanel;
|
||||||
wxToolBar* imagerToolbar;
|
wxAuiToolBar* imagerToolbar;
|
||||||
wxToolBarToolBase* imagerBackTool;
|
wxAuiToolBarItem* imagerBackTool;
|
||||||
VisualisationControl* visualiser;
|
VisualisationControl* visualiser;
|
||||||
wxButton* imagerSaveImageButton;
|
wxButton* imagerSaveImageButton;
|
||||||
wxButton* imagerSaveFluxButton;
|
wxButton* imagerSaveFluxButton;
|
||||||
wxStaticText* m_staticText4;
|
wxStaticText* m_staticText4;
|
||||||
wxButton* imagerGoAgainButton;
|
wxButton* imagerGoAgainButton;
|
||||||
wxPanel* browsePanel;
|
wxPanel* browsePanel;
|
||||||
wxToolBar* browserToolbar;
|
wxAuiToolBar* browserToolbar;
|
||||||
wxToolBarToolBase* browserBackTool;
|
wxAuiToolBarItem* browserBackTool;
|
||||||
wxScrolledWindow* m_scrolledWindow1;
|
wxAuiToolBarItem* browserInfoTool;
|
||||||
wxDataViewCtrl* browserView;
|
wxAuiToolBarItem* browserViewTool;
|
||||||
wxDataViewColumn* browserFilenameColumn;
|
wxAuiToolBarItem* browserSaveTool;
|
||||||
wxDataViewColumn* browserModeColumn;
|
wxAuiToolBarItem* browserMoreMenuButton;
|
||||||
wxDataViewColumn* browserLengthColumn;
|
wxMenu* browserMoreMenu;
|
||||||
wxDataViewColumn* browserExtraColumn;
|
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* browserDiscardButton;
|
||||||
wxButton* browserCommitButton;
|
wxButton* browserCommitButton;
|
||||||
|
wxStaticText* m_staticText12;
|
||||||
|
|
||||||
// Virtual event handlers, override them in your derived class
|
// Virtual event handlers, override them in your derived class
|
||||||
virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
|
virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
|
||||||
@@ -105,18 +120,49 @@ class MainWindowGen : public wxFrame
|
|||||||
virtual void OnCustomConfigurationButton( wxCommandEvent& event ) { event.Skip(); }
|
virtual void OnCustomConfigurationButton( wxCommandEvent& event ) { event.Skip(); }
|
||||||
virtual void OnReadButton( wxCommandEvent& event ) { event.Skip(); }
|
virtual void OnReadButton( wxCommandEvent& event ) { event.Skip(); }
|
||||||
virtual void OnWriteButton( 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 OnBackButton( wxCommandEvent& event ) { event.Skip(); }
|
||||||
virtual void OnSaveImageButton( wxCommandEvent& event ) { event.Skip(); }
|
virtual void OnSaveImageButton( wxCommandEvent& event ) { event.Skip(); }
|
||||||
virtual void OnSaveFluxButton( wxCommandEvent& event ) { event.Skip(); }
|
virtual void OnSaveFluxButton( wxCommandEvent& event ) { event.Skip(); }
|
||||||
virtual void OnImagerGoAgainButton( 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:
|
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();
|
~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:
|
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();
|
~TextViewerWindowGen();
|
||||||
|
|
||||||
@@ -164,7 +210,7 @@ class FluxViewerWindowGen : public wxDialog
|
|||||||
|
|
||||||
public:
|
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();
|
~FluxViewerWindowGen();
|
||||||
|
|
||||||
@@ -191,9 +237,158 @@ class TextEditorWindowGen : public wxDialog
|
|||||||
|
|
||||||
public:
|
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();
|
~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();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,11 +15,15 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "fluxviewerwindow.h"
|
#include "fluxviewerwindow.h"
|
||||||
#include "textviewerwindow.h"
|
#include "textviewerwindow.h"
|
||||||
|
#include "fileviewerwindow.h"
|
||||||
#include "texteditorwindow.h"
|
#include "texteditorwindow.h"
|
||||||
|
#include "filesystemmodel.h"
|
||||||
#include "customstatusbar.h"
|
#include "customstatusbar.h"
|
||||||
|
#include "lib/vfs/vfs.h"
|
||||||
#include <google/protobuf/text_format.h>
|
#include <google/protobuf/text_format.h>
|
||||||
#include <wx/config.h>
|
#include <wx/config.h>
|
||||||
#include <wx/aboutdlg.h>
|
#include <wx/aboutdlg.h>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
extern const std::map<std::string, std::string> formats;
|
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"
|
"# or the name of a built-in configuration, or the filename\n"
|
||||||
"# of a text proto file. Or a comment, of course.\n\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
|
class MainWindow : public MainWindowGen
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
class FilesystemOperation;
|
||||||
|
|
||||||
public:
|
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(
|
Logger::setLogger(
|
||||||
[&](std::shared_ptr<const AnyLogMessage> message)
|
[&](std::shared_ptr<const AnyLogMessage> message)
|
||||||
@@ -96,6 +115,26 @@ public:
|
|||||||
emergencyStop = true;
|
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
|
/* I have no idea why this is necessary, but on Windows things aren't
|
||||||
* laid out correctly without it. */
|
* laid out correctly without it. */
|
||||||
|
|
||||||
@@ -193,8 +232,8 @@ public:
|
|||||||
|
|
||||||
void OnControlsChanged(wxFileDirPickerEvent& event)
|
void OnControlsChanged(wxFileDirPickerEvent& event)
|
||||||
{
|
{
|
||||||
SaveConfig();
|
wxCommandEvent e;
|
||||||
UpdateState();
|
OnControlsChanged(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnReadButton(wxCommandEvent&)
|
void OnReadButton(wxCommandEvent&)
|
||||||
@@ -257,6 +296,8 @@ public:
|
|||||||
|
|
||||||
ImageReader::updateConfigForFilename(
|
ImageReader::updateConfigForFilename(
|
||||||
config.mutable_image_reader(), filename.ToStdString());
|
config.mutable_image_reader(), filename.ToStdString());
|
||||||
|
ImageWriter::updateConfigForFilename(
|
||||||
|
config.mutable_image_writer(), filename.ToStdString());
|
||||||
visualiser->Clear();
|
visualiser->Clear();
|
||||||
_currentDisk = nullptr;
|
_currentDisk = nullptr;
|
||||||
|
|
||||||
@@ -394,6 +435,605 @@ public:
|
|||||||
UpdateState();
|
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
|
/* This sets the *global* config object. That's safe provided the worker
|
||||||
* thread isn't running, otherwise you'll get a race. */
|
* thread isn't running, otherwise you'll get a race. */
|
||||||
void PrepareConfig()
|
void PrepareConfig()
|
||||||
@@ -508,6 +1148,7 @@ public:
|
|||||||
_statusBar->HideProgressBar();
|
_statusBar->HideProgressBar();
|
||||||
_statusBar->SetRightLabel("");
|
_statusBar->SetRightLabel("");
|
||||||
_state = _errorState;
|
_state = _errorState;
|
||||||
|
_filesystemQueue.clear();
|
||||||
UpdateState();
|
UpdateState();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -690,8 +1331,6 @@ public:
|
|||||||
|
|
||||||
void UpdateState()
|
void UpdateState()
|
||||||
{
|
{
|
||||||
bool running = wxGetApp().IsWorkerThreadRunning();
|
|
||||||
|
|
||||||
if (_state < STATE_IDLE__LAST)
|
if (_state < STATE_IDLE__LAST)
|
||||||
{
|
{
|
||||||
dataNotebook->SetSelection(0);
|
dataNotebook->SetSelection(0);
|
||||||
@@ -724,6 +1363,36 @@ public:
|
|||||||
else if (_state < STATE_BROWSING__LAST)
|
else if (_state < STATE_BROWSING__LAST)
|
||||||
{
|
{
|
||||||
dataNotebook->SetSelection(2);
|
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();
|
Refresh();
|
||||||
@@ -785,11 +1454,66 @@ private:
|
|||||||
STATE_BROWSING__LAST
|
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;
|
wxConfig _config;
|
||||||
|
wxDataFormat _dndFormat;
|
||||||
std::vector<std::pair<std::string, std::unique_ptr<const ConfigProto>>>
|
std::vector<std::pair<std::string, std::unique_ptr<const ConfigProto>>>
|
||||||
_formats;
|
_formats;
|
||||||
std::vector<std::unique_ptr<const CandidateDevice>> _devices;
|
std::vector<std::unique_ptr<const CandidateDevice>> _devices;
|
||||||
int _state;
|
int _state = STATE_IDLE;
|
||||||
int _errorState;
|
int _errorState;
|
||||||
int _selectedSource;
|
int _selectedSource;
|
||||||
bool _dontSaveConfig = false;
|
bool _dontSaveConfig = false;
|
||||||
@@ -799,6 +1523,12 @@ private:
|
|||||||
std::unique_ptr<TextViewerWindow> _logWindow;
|
std::unique_ptr<TextViewerWindow> _logWindow;
|
||||||
std::unique_ptr<TextViewerWindow> _configWindow;
|
std::unique_ptr<TextViewerWindow> _configWindow;
|
||||||
std::string _extraConfiguration;
|
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()
|
wxWindow* FluxEngineApp::CreateMainWindow()
|
||||||
|
|||||||
@@ -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, FATFS)
|
||||||
$(call use-library, $(OBJDIR)/tests/$1.exe, $(OBJDIR)/tests/$1.o, ADFLIB)
|
$(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, HFSUTILS)
|
||||||
|
$(call use-library, $(OBJDIR)/tests/$1.exe, $(OBJDIR)/tests/$1.o, LIBUSBP)
|
||||||
|
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
|||||||
28
tests/vfs.cc
28
tests/vfs.cc
@@ -6,17 +6,27 @@ using namespace snowhouse;
|
|||||||
|
|
||||||
static void testPathParsing()
|
static void testPathParsing()
|
||||||
{
|
{
|
||||||
AssertThat(Path(""), Equals(std::vector<std::string>{}));
|
AssertThat(Path(""), Equals(std::vector<std::string>{}));
|
||||||
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"), 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("/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)
|
int main(void)
|
||||||
{
|
{
|
||||||
testPathParsing();
|
testPathParsing();
|
||||||
return 0;
|
testPathParenthood();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user