diff --git a/Makefile b/Makefile index 0507e292..3f37ae20 100644 --- a/Makefile +++ b/Makefile @@ -177,38 +177,40 @@ endef $(call do-encodedecodetest,agat840) $(call do-encodedecodetest,amiga) -$(call do-encodedecodetest,appleii140) -$(call do-encodedecodetest,atarist360) -$(call do-encodedecodetest,atarist370) -$(call do-encodedecodetest,atarist400) -$(call do-encodedecodetest,atarist410) -$(call do-encodedecodetest,atarist720) -$(call do-encodedecodetest,atarist740) -$(call do-encodedecodetest,atarist800) -$(call do-encodedecodetest,atarist820) +$(call do-encodedecodetest,apple2,,--140) +$(call do-encodedecodetest,atarist,,--360) +$(call do-encodedecodetest,atarist,,--370) +$(call do-encodedecodetest,atarist,,--400) +$(call do-encodedecodetest,atarist,,--410) +$(call do-encodedecodetest,atarist,,--720) +$(call do-encodedecodetest,atarist,,--740) +$(call do-encodedecodetest,atarist,,--800) +$(call do-encodedecodetest,atarist,,--820) $(call do-encodedecodetest,bk800) -$(call do-encodedecodetest,brother120) -$(call do-encodedecodetest,brother240) -$(call do-encodedecodetest,commodore1541,scripts/commodore1541_test.textpb,--35) -$(call do-encodedecodetest,commodore1541,scripts/commodore1541_test.textpb,--40) +$(call do-encodedecodetest,brother,,--120) +$(call do-encodedecodetest,brother,,--240) +$(call do-encodedecodetest,commodore1541,scripts/commodore1541_test.textpb,--171) +$(call do-encodedecodetest,commodore1541,scripts/commodore1541_test.textpb,--192) $(call do-encodedecodetest,commodore1581) $(call do-encodedecodetest,cmd_fd2000) -$(call do-encodedecodetest,hp9121) -$(call do-encodedecodetest,ibm1200) -$(call do-encodedecodetest,ibm1232) -$(call do-encodedecodetest,ibm1440) -$(call do-encodedecodetest,ibm180) -$(call do-encodedecodetest,ibm160) -$(call do-encodedecodetest,ibm320) -$(call do-encodedecodetest,ibm360) -$(call do-encodedecodetest,ibm720) -$(call do-encodedecodetest,mac400,scripts/mac400_test.textpb) -$(call do-encodedecodetest,mac800,scripts/mac800_test.textpb) +$(call do-encodedecodetest,hplif,,--264) +$(call do-encodedecodetest,hplif,,--616) +$(call do-encodedecodetest,hplif,,--770) +$(call do-encodedecodetest,ibm,,--1200) +$(call do-encodedecodetest,ibm,,--1232) +$(call do-encodedecodetest,ibm,,--1440) +$(call do-encodedecodetest,ibm,,--180) +$(call do-encodedecodetest,ibm,,--160) +$(call do-encodedecodetest,ibm,,--320) +$(call do-encodedecodetest,ibm,,--360) +$(call do-encodedecodetest,ibm,,--720) +$(call do-encodedecodetest,mac,scripts/mac400_test.textpb,--400) +$(call do-encodedecodetest,mac,scripts/mac800_test.textpb,--800) $(call do-encodedecodetest,n88basic) $(call do-encodedecodetest,rx50) $(call do-encodedecodetest,tids990) -$(call do-encodedecodetest,victor9k_ss) -$(call do-encodedecodetest,victor9k_ds) +$(call do-encodedecodetest,victor9k,,--612) +$(call do-encodedecodetest,victor9k,,--1224) $(OBJDIR)/%.a: @mkdir -p $(dir $@) diff --git a/doc/disk-ampro.md b/doc/disk-ampro.md index 0d5091a9..d8f1f295 100644 --- a/doc/disk-ampro.md +++ b/doc/disk-ampro.md @@ -19,13 +19,15 @@ Reading discs Just do: ``` -fluxengine read ampro400 +fluxengine read ampro ``` -You should end up with an `ampro.img` which is 409600. If you have a -double-sided disk, use `ampro800`, which will give you a file819200 bytes long. -These is an alias for `fluxengine read ibm` with preconfigured parameters. You -can pass this straight into [cpmtools](http://www.moria.de/~michael/cpmtools/): +...where `` is one of `--400` for a 40-track disk or `--800` for an +80-track disk. These are an alias for `fluxengine read ibm` with preconfigured +parameters. + +FluxEngine has direct filesystem support for these disks, or you can pass the +disk images into [cpmtools](http://www.moria.de/~michael/cpmtools/): ``` $ cpmls -f ampdsdd ampro.img diff --git a/doc/disk-apple2.md b/doc/disk-apple2.md index eae99f4f..d1ac701d 100644 --- a/doc/disk-apple2.md +++ b/doc/disk-apple2.md @@ -30,7 +30,8 @@ FluxEngine can remap the sectors from physical to logical using modifiers. If you don't specify a remapping modifier, you get the sectors in the order they appear on the disk. -If you don't want an image in physical sector order, specify one of these options: +If you don't want an image in physical sector order, specify one of these +options: - `--appledos` Selects AppleDOS sector translation - `--prodos` Selects ProDOS sector translation @@ -40,15 +41,21 @@ These options also select the appropriate file system; FluxEngine has read-only support for all of these. For example: ``` -fluxengine ls appleii140 --appledos -f image.flux +fluxengine ls apple2 --appledos -f image.flux ``` In addition, some third-party systems use 80-track double sides drives, with -the same underlying disk format. These are supported with the `appleii640` -profile. The complication here is that the AppleDOS filesystem only supports up +the same underlying disk format. The full list of formats supported is: + + - `--140` 35-track single-sided (the normal Apple II format) + - `--640` 80-track double-sided + +`--140` is the default. + +The complication here is that the AppleDOS filesystem only supports up to 50 tracks, so it needs tweaking to support larger disks. It treats the second side of the disk as a completely different volume. To access these -files, use `--appledos --side1`. +files, use `--640 --appledos --side1`. [^1]: CP/M disks use the ProDOS translation for the first three tracks and a different translation for all the tracks thereafter. @@ -65,12 +72,12 @@ Reading discs Just do: ``` -fluxengine read appleii140 +fluxengine read apple2 ``` -(or `appleii640`) +(or `apple2 --640`) -You should end up with an `appleii140.img` which is 143360 bytes long. It will +You should end up with an `apple2.img` which is 143360 bytes long. It will be in physical sector ordering if you don't specify a file system format as described above. @@ -79,7 +86,7 @@ Writing discs Just do: ``` -fluxengine write appleii140 -i appleii140.img +fluxengine write apple2 -i apple2.img ``` The image will be expected to be in physical sector ordering if you don't diff --git a/doc/disk-atarist.md b/doc/disk-atarist.md index 6a43241a..b09798f5 100644 --- a/doc/disk-atarist.md +++ b/doc/disk-atarist.md @@ -33,29 +33,29 @@ FluxEngine can also write Atari ST scheme disks. The syntax is: - fluxengine write -i input.st + fluxengine write atarist -i input.st Available formats ----------------- `` can be one of these: - - `atarist360`: a 360kB 3.5" disk, with 80 cylinders, 1 side, and 9 sectors - per track. - - `atarist370`: a 370kB 3.5" disk, with 82 cylinders, 1 side, and 9 sectors - per track. - - `atarist400`: a 400kB 3.5" disk, with 80 cylinders, 1 side, and 10 sectors - per track. - - `atarist410`: a 410kB 3.5" disk, with 82 cylinders, 1 side, and 10 sectors - per track. - - `atarist720`: a 720kB 3.5" disk, with 80 cylinders, 2 sides, and 9 sectors - per track. - - `atarist740`: a 740kB 3.5" disk, with 82 cylinders, 2 sides, and 9 sectors - per track. - - `atarist800`: a 800kB 3.5" disk, with 80 cylinders, 2 sides, and 10 sectors - per track. - - `atarist820`: a 820kB 3.5" disk, with 82 cylinders, 2 sides, and 10 sectors - per track. + - `--360`: a 360kB 3.5" disk, with 80 cylinders, 1 side, and 9 sectors per + track. + - `--370`: a 370kB 3.5" disk, with 82 cylinders, 1 side, and 9 sectors per + track. + - `--400`: a 400kB 3.5" disk, with 80 cylinders, 1 side, and 10 sectors per + track. + - `--410`: a 410kB 3.5" disk, with 82 cylinders, 1 side, and 10 sectors per + track. + - `--720`: a 720kB 3.5" disk, with 80 cylinders, 2 sides, and 9 sectors per + track. + - `--740`: a 740kB 3.5" disk, with 82 cylinders, 2 sides, and 9 sectors per + track. + - `--800`: a 800kB 3.5" disk, with 80 cylinders, 2 sides, and 10 sectors per + track. + - `--820`: a 820kB 3.5" disk, with 82 cylinders, 2 sides, and 10 sectors per + track. See [the IBM format documentation](disk-ibm.md) for more information. Note that only some PC 3.5" floppy disk drives are capable of seeking to the 82nd track. diff --git a/doc/disk-brother.md b/doc/disk-brother.md index ba6897af..0a04acaa 100644 --- a/doc/disk-brother.md +++ b/doc/disk-brother.md @@ -38,10 +38,10 @@ Reading disks Just do: ``` -fluxengine read `` +fluxengine read brother `` ``` -... where `` can be `brother120` or `brother240`. You should end up +... where `` can be `--120` or `--240`. You should end up with a `brother.img` which is either 119808 or 239616 bytes long. Writing disks @@ -53,7 +53,7 @@ Just do: fluxengine write `` -i brother.img ``` -...where `` can be `brother120` or `brother240`. +...where `` can be `--120` or `--240`. Dealing with misaligned disks ----------------------------- @@ -102,7 +102,8 @@ record.) The sector order is 05a3816b4927, which gives a sector skew of 5. High level format ----------------- -Once decoded, you end up with a file system image. +Once decoded, you end up with a file system image. FluxEngine supports direct +filesystem access for both kinds of disks. ### 120kB disks diff --git a/doc/disk-c64.md b/doc/disk-c64.md index d38bb231..38eafa35 100644 --- a/doc/disk-c64.md +++ b/doc/disk-c64.md @@ -33,29 +33,28 @@ Reading 1541 disks Just do: ``` -fluxengine read commodore1541 -o commodore1541.d64 +fluxengine read commodore -o commodore.d64 ``` -You should end up with an `commodore1541.d64` file which is 174848 bytes long. +You should end up with an `commodore.d64` file which is 174848 bytes long. You can load this straight into a Commodore 64 emulator such as [VICE](http://vice-emu.sourceforge.net/). -If you have a 40-track disk, add `--40`. +If you have a 40-track disk, add `--196`. **Big warning!** Commodore 64 disk images are complicated due to the way the tracks are different sizes and the odd sector size, so you need the special D64 -or LDBS output formats to represent them sensibly. Don't use IMG unless you -know what you're doing. +or LDBS output formats to represent them sensibly. Writing 1541 disks ------------------ Just do: ``` -fluxengine write commodore1541 -i file.d64 +fluxengine write commodore -i file.d64 ``` -If you have a 40-track disk, add `--40`. +If you have a 40-track disk, add `--196`. Note that only standard Commodore 64 BAM file systems can be written this way, as the disk ID in the BAM has to be copied to every sector on the disk. diff --git a/doc/disk-ibm.md b/doc/disk-ibm.md index baaed48f..b8953f08 100644 --- a/doc/disk-ibm.md +++ b/doc/disk-ibm.md @@ -38,15 +38,16 @@ Reading disks Just do: - fluxengine read `` + fluxengine read ibm `` -...and you'll end up with a `.img` file. This should work on most PC -disks (including FM 360kB disks, 3.5" 1440kB disks, 5.25" 1200kB disks, etc.) -The size of the disk image will vary depending on the format. +...and you'll end up with an `ibm.img` file. You'll need to specify which +format to use; this can be one of `--160`, `--180`, `--320`, `--360`, `--720`, +`--1200`, `--1232` or `--1400` depending. The size of the disk image will vary +depending on the format. -The common PC formats are `ibm720` and `ibm1440`, but there are _many_ others, +The common PC formats are `--720` and `--1440`, but there are _many_ others, and there's too many configuration options to usefully list. Use `fluxengine -write` to list all formats, and try `fluxengine write ibm1440 --config` to see +write` to list all formats, and try `fluxengine write ibm --1440 --config` to see a sample configuration. Configuration options you'll want include: @@ -84,16 +85,13 @@ makes things slightly awkward. Preconfigured profiles are available. The syntax is: - fluxengine write -i input.img + fluxengine write ibm -i input.img -The common PC formats are `ibm720` and `ibm1440`, but there are _many_ others, -and there's too many configuration options to usefully list. Use `fluxengine -write` to list all formats, and try `fluxengine write ibm1440 --config` to see -a sample configuration. +See above for the formats. Some image formats, such as DIM, specify the image format, For these you can -specify the `ibm` format and FluxEngine will automatically determine the -correct format to use. +specify the `--auto` format (which is the default) and FluxEngine will +automatically determine the correct format to use. Mixed-format disks ------------------ @@ -131,11 +129,7 @@ drives, feature "tri-mode" support which in addition to normal 300rpm modes, can change their speed to read and write 360rpm DD and HD disks. Neither the FluxEngine or Greaseweazle hardware can currently command a -tri-mode drive to spin at 360rpm, however an older 360rpm-only drive will work -to read these formats. +tri-mode drive to spin at 360rpm. However on both devices the FluxEngine +software is capable of both reading and writing 300rpm disks at 360rpm and vice +versa, so it shouldn't matter. -Alternately, the FluxEngine software can rescale the flux pulses to enable -reading and writing these formats with a plain 300rpm drive. To do this, -specify the following two additional options: - - --flux_source.rescale=1.2 --flux_sink.rescale=1.2 diff --git a/doc/disk-macintosh.md b/doc/disk-macintosh.md index 83607c51..2705ff7c 100644 --- a/doc/disk-macintosh.md +++ b/doc/disk-macintosh.md @@ -37,20 +37,19 @@ Reading discs Just do: ``` -fluxengine read -o mac.dsk +fluxengine read mac -o mac.dsk ``` -...where `` can be `mac400` or `mac800`. +...where `` can be `--400` or `--800`. You should end up with a `mac.dsk` file containing a raw sector image (equivalent to `.img`). -**Big warning!** Mac disk images are complicated due to the way the tracks are -different sizes and the odd sector size. What you get above is a triangular -disk image, which contains all the 512-byte user data areas concatenated -together in filesystem order. It does not contain the twelve bytes of metadata. -If you want these as well, specify that you want 524 byte sectors with -`--output.image.img.trackdata.sector_size=512`. The metadata will follow the +The Mac disk format contains an extra twelve bytes of data per sector which can +be used for filesystem metadata. In practice, this was never used by anyone, +and so the default is to omit these. If you want them, specify that you want +524 byte sectors with `--layout.layoutdata.sector_size=524`. The metadata will +follow the 512 bytes of user data. FluxEngine also supports DiskCopy 4.2 disk images, which may be a better option @@ -64,16 +63,13 @@ Writing discs Just do: ``` -fluxengine write -i mac.dsk +fluxengine write mac -i mac.dsk ``` -...where `` can be `mac400` or `mac800`. +...where `` can be `400` or `800`. It'll read the image file and write it out. -The same warning as above applies --- you can use normal `.dsk` files but it's -problematic. Consider using DiskCopy 4.2 files instead. - Useful references ----------------- diff --git a/doc/disk-micropolis.md b/doc/disk-micropolis.md index 125b12c9..0938df79 100644 --- a/doc/disk-micropolis.md +++ b/doc/disk-micropolis.md @@ -23,10 +23,10 @@ Reading disks Based on your floppy drive, just do one of: ``` -fluxengine read micropolis143 # single-sided Mod I -fluxengine read micropolis287 # double-sided Mod I -fluxengine read micropolis315 # single-sided Mod II -fluxengine read micropolis630 # double-sided Mod II +fluxengine read micropolis --143 # single-sided Mod I +fluxengine read micropolis --287 # double-sided Mod I +fluxengine read micropolis --315 # single-sided Mod II +fluxengine read micropolis --630 # double-sided Mod II ``` You should end up with a `micropolis.img` of the corresponding size. The image @@ -45,10 +45,10 @@ It's also possible to output to VGI, which retains OS-specific "user data" and machine-specific ECC. Add `--vgi` to the command line after the chosen Micropolis profile: ``` -fluxengine read micropolis143 --vgi # single-sided Mod I -fluxengine read micropolis287 --vgi # double-sided Mod I -fluxengine read micropolis315 --vgi # single-sided Mod II -fluxengine read micropolis630 --vgi # double-sided Mod II +fluxengine read micropolis --143 --vgi # single-sided Mod I +fluxengine read micropolis --287 --vgi # double-sided Mod I +fluxengine read micropolis --315 --vgi # single-sided Mod II +fluxengine read micropolis --630 --vgi # double-sided Mod II ``` You should end up with a `micropolis.vgi` instead. The format is well-defined @@ -72,15 +72,15 @@ Writing disks Just do one of: ``` -fluxengine write micropolis143 # single-sided Mod I -fluxengine write micropolis287 # double-sided Mod I -fluxengine write micropolis315 # single-sided Mod II -fluxengine write micropolis630 # double-sided Mod II +fluxengine write micropolis --143 # single-sided Mod I +fluxengine write micropolis --287 # double-sided Mod I +fluxengine write micropolis --315 # single-sided Mod II +fluxengine write micropolis --630 # double-sided Mod II -fluxengine write micropolis143 --vgi # single-sided Mod I -fluxengine write micropolis287 --vgi # double-sided Mod I -fluxengine write micropolis315 --vgi # single-sided Mod II -fluxengine write micropolis630 --vgi # double-sided Mod II +fluxengine write micropolis --143 --vgi # single-sided Mod I +fluxengine write micropolis --287 --vgi # double-sided Mod I +fluxengine write micropolis --315 --vgi # single-sided Mod II +fluxengine write micropolis --630 --vgi # double-sided Mod II ``` Useful references diff --git a/doc/disk-mx.md b/doc/disk-mx.md index d93f0571..558b5b1f 100644 --- a/doc/disk-mx.md +++ b/doc/disk-mx.md @@ -9,7 +9,7 @@ clone of the PDP-11. The MX board was an early floppy drive controller board for it.
-A Durango F85, held precariously +A DVK computer
The MX format is interesting in that it has to be read a track at a time. The @@ -42,15 +42,17 @@ Reading discs ------------- ``` -fluxengine read mx440 +fluxengine read mx ``` -You should end up with an `mx.img` which will vary in length depending on the format. The default is double-sided 80-track. For the other formats, use: +You should end up with an `mx.img` which will vary in length depending on the +format. The default is double-sided 80-track. For the other formats, add one of +the following options: - * single-sided 40-track: `mx110` - * double-sided 40-track: `mx220_ds` - * single-sided 80-track: `mx220_ss` - * double-sided 80-track: `mx440` + * single-sided 40-track: `--110` + * double-sided 40-track: `--220ds` + * single-sided 80-track: `--220ss` + * double-sided 80-track: `--440` Useful references diff --git a/doc/disk-northstar.md b/doc/disk-northstar.md index 7ddfdc09..a3220d6a 100644 --- a/doc/disk-northstar.md +++ b/doc/disk-northstar.md @@ -21,12 +21,10 @@ equivalent to .img images. Reading disks ------------- -You must use a 48-TPI (40-track) 300RPM 5.25" floppy drive. - To read a double-sided North Star floppy, run: ``` -fluxengine read +fluxengine read northstar ``` ...where `` is one of the formats listed below. @@ -37,13 +35,10 @@ disk type. Writing disks ------------- -You must use a 48-TPI (40-track) 300RPM 5.25" floppy drive and make -sure that the drive's spindle speed is adjusted to exactly 300RPM. - To write a double-sided North Star floppy, run: ``` -fluxengine write -i image_to_write.nsi +fluxengine write northstar -i image_to_write.nsi ``` ...where `` is one of the formats listed below. @@ -55,9 +50,9 @@ The following formats are supported: | Format name | Disk Type | File Size (bytes) | | -------------- | ----------------------------------- | ----------------- | -| `northstar87` | Single-Sided, Single-Density (SSSD) | 89,600 | -| `northstar175` | Single-Sided, Double-Density (SSDD) | 179,200 | -| `northstar350` | Double-Sided, Double-Density (DSDD) | 358,400 | +| `--87` | Single-Sided, Single-Density (SSSD) | 89,600 | +| `--175` | Single-Sided, Double-Density (SSDD) | 179,200 | +| `--350` | Double-Sided, Double-Density (DSDD) | 358,400 | Useful references ----------------- diff --git a/doc/disk-victor9k.md b/doc/disk-victor9k.md index 9bba7dc5..0451b8a4 100644 --- a/doc/disk-victor9k.md +++ b/doc/disk-victor9k.md @@ -8,7 +8,7 @@ sector GCR disks, with a variable-speed drive and a varying number of sectors per track --- from 19 to 12. Disks can be double-sided, meaning that they can store 1224kB per disk, which was almost unheard of back then. Because the way that the tracks on head 1 are offset from head 0 (this happens with all disks), -the speed zone allocation on head 1 differ from head 0... +the speed zone allocation on head 1 differs from head 0... | Zone | Head 0 tracks | Head 1 tracks | Sectors | Original period (ms) | |:----:|:-------------:|:-------------:|:-------:|:--------------------:| @@ -40,18 +40,12 @@ Reading discs Just do: ``` -fluxengine read +fluxengine read victor9k ``` -...where `` can be `victor9k_ss` or `victor9k_ds`. - -For `victor9k_ss` you should end up with an `victor9k.img` which is 627200 bytes long. -For `victor9k_ds` you should end up with an `victor9k.img` which is 1224192 bytes long. - -**Big warning!** The image is triangular, where each track occupies a different -amount of space. Victor disk images are complicated due to the way the tracks -are different sizes and the odd sector size. +...where `` can be `--612` for a single-sided disk or `--1224` for a +double-sided disk. Writing discs ------------- @@ -59,11 +53,10 @@ Writing discs Just do: ``` -fluxengine read victor9k_ss -i victor9k.img +fluxengine write victor9k -i victor9k.img ``` -**Big warning!** This uses the same triangular disk image that reading uses. - +`` is as above. Useful references ----------------- diff --git a/lib/build.mk b/lib/build.mk index b05ad524..8fc6b76e 100644 --- a/lib/build.mk +++ b/lib/build.mk @@ -77,6 +77,7 @@ LIBFLUXENGINE_SRCS = \ lib/vfs/cbmfs.cc \ lib/vfs/cpmfs.cc \ lib/vfs/fatfs.cc \ + lib/vfs/lif.cc \ lib/vfs/machfs.cc \ lib/vfs/prodos.cc \ lib/vfs/smaky6fs.cc \ diff --git a/lib/config.proto b/lib/config.proto index c523384d..eb2a045d 100644 --- a/lib/config.proto +++ b/lib/config.proto @@ -12,7 +12,7 @@ import "lib/drive.proto"; import "lib/common.proto"; import "lib/layout.proto"; -// NEXT_TAG: 21 +// NEXT_TAG: 23 message ConfigProto { optional string comment = 8; @@ -39,18 +39,27 @@ message ConfigProto optional FilesystemProto filesystem = 17; repeated OptionProto option = 20; + repeated OptionGroupProto option_group = 22; } +// NEXT_TAG: 7 message OptionProto { optional string name = 1 [ (help) = "option name" ]; optional string comment = 2 [ (help) = "help text for option" ]; optional string message = 3 [ (help) = "message to display when option is in use" ]; - optional string exclusivity_group = 5 [ - (help) = - "options with the same group cannot be selected at the same time" - ]; + optional bool set_by_default = 6 [ + (help) = "this option is applied by default", + default = false + ]; optional ConfigProto config = 4 [ (help) = "option data", (recurse) = false ]; } + +message OptionGroupProto +{ + optional string comment = 1 [ (help) = "help text for option group" ]; + repeated OptionProto option = 2; +} + diff --git a/lib/flags.cc b/lib/flags.cc index 7953b731..f9ece11f 100644 --- a/lib/flags.cc +++ b/lib/flags.cc @@ -1,364 +1,424 @@ -#include "globals.h" -#include "flags.h" -#include "proto.h" -#include "utils.h" -#include "logger.h" -#include "fmt/format.h" -#include -#include -#include - -static FlagGroup* currentFlagGroup; -static std::vector all_flags; -static std::map flags_by_name; - -static void doHelp(); -static void doShowConfig(); -static void doDoc(); - -static FlagGroup helpGroup; -static ActionFlag helpFlag = ActionFlag({"--help"}, "Shows the help.", doHelp); - -static ActionFlag showConfigFlag = ActionFlag({"--config", "-C"}, - "Shows the currently set configuration and halts.", - doShowConfig); - -static ActionFlag docFlag = ActionFlag( - {"--doc"}, "Shows the available configuration options and halts.", doDoc); - -FlagGroup::FlagGroup() -{ - currentFlagGroup = this; -} - -FlagGroup::FlagGroup(std::initializer_list groups): _groups(groups) -{ - currentFlagGroup = this; -} - -void FlagGroup::addFlag(Flag* flag) -{ - _flags.push_back(flag); -} - -static bool setFallbackFlag( - const std::string& key, const std::string& value, bool uses_that) -{ - if (beginsWith(key, "--")) - { - std::string path = key.substr(2); - if (key.find('.') != std::string::npos) - { - ProtoField protoField = resolveProtoPath(&config, path); - setProtoFieldFromString(protoField, value); - return uses_that; - } - else - { - if (FlagGroup::applyOption(path)) - return false; - } - } - Error() << "unrecognised flag; try --help"; -} - -bool FlagGroup::applyOption(const std::string& option) -{ - for (const auto& configs : config.option()) - { - if (option == configs.name()) - { - if (configs.config().option_size() > 0) - Error() << fmt::format( - "option '{}' has an option inside it, which isn't " - "allowed", - option); - if (configs.config().include_size() > 0) - Error() << fmt::format( - "option '{}' is trying to include something, which " - "isn't allowed", - option); - - Logger() << fmt::format("OPTION: {}", configs.message()); - config.MergeFrom(configs.config()); - return true; - } - } - - return false; -} - -std::vector FlagGroup::parseFlagsWithFilenames(int argc, - const char* argv[], - std::function callback) -{ - if (_initialised) - throw std::runtime_error("called parseFlags() twice"); - - /* Recursively accumulate a list of all flags. */ - - all_flags.clear(); - flags_by_name.clear(); - std::function recurse; - recurse = [&](FlagGroup* group) - { - if (group->_initialised) - return; - - for (FlagGroup* subgroup : group->_groups) - recurse(subgroup); - - for (Flag* flag : group->_flags) - { - for (const auto& name : flag->names()) - { - if (flags_by_name.find(name) != flags_by_name.end()) - Error() << "two flags use the name '" << name << "'"; - flags_by_name[name] = flag; - } - - all_flags.push_back(flag); - } - - group->_initialised = true; - }; - recurse(this); - recurse(&helpGroup); - - /* Now actually parse them. */ - - std::vector filenames; - int index = 1; - while (index < argc) - { - std::string thisarg = argv[index]; - std::string thatarg = (index < (argc - 1)) ? argv[index + 1] : ""; - - std::string key; - std::string value; - bool usesthat = false; - - if (thisarg.size() == 0) - { - /* Ignore this argument. */ - } - else if (thisarg[0] != '-') - { - /* This is a filename. Pass it to the callback, and if not consumed - * queue it. */ - if (!callback(thisarg)) - filenames.push_back(thisarg); - } - else - { - /* This is a flag. */ - - if ((thisarg.size() > 1) && (thisarg[1] == '-')) - { - /* Long option. */ - - auto equals = thisarg.rfind('='); - if (equals != std::string::npos) - { - key = thisarg.substr(0, equals); - value = thisarg.substr(equals + 1); - } - else - { - key = thisarg; - value = thatarg; - usesthat = true; - } - } - else - { - /* Short option. */ - - if (thisarg.size() > 2) - { - key = thisarg.substr(0, 2); - value = thisarg.substr(2); - } - else - { - key = thisarg; - value = thatarg; - usesthat = true; - } - } - - auto flag = flags_by_name.find(key); - if (flag == flags_by_name.end()) - { - if (setFallbackFlag(key, value, usesthat)) - index++; - } - else - { - flag->second->set(value); - if (usesthat && flag->second->hasArgument()) - index++; - } - } - - index++; - } - - return filenames; -} - -void FlagGroup::parseFlags(int argc, - const char* argv[], - std::function callback) -{ - auto filenames = parseFlagsWithFilenames(argc, argv, callback); - if (!filenames.empty()) - Error() << "non-option parameter " << *filenames.begin() - << " seen (try --help)"; -} - -void FlagGroup::parseFlagsWithConfigFiles(int argc, - const char* argv[], - const std::map& configFiles) -{ - parseFlags(argc, - argv, - [&](const auto& filename) - { - parseConfigFile(filename, configFiles); - return true; - }); -} - -ConfigProto FlagGroup::parseSingleConfigFile(const std::string& filename, - const std::map& configFiles) -{ - const auto& it = configFiles.find(filename); - if (it != configFiles.end()) - { - ConfigProto config; - if (!config.ParseFromString(it->second)) - Error() << "couldn't load built-in config proto"; - return config; - } - else - { - std::ifstream f(filename, std::ios::out); - if (f.fail()) - Error() << fmt::format( - "Cannot open '{}': {}", filename, strerror(errno)); - - std::ostringstream ss; - ss << f.rdbuf(); - - ConfigProto config; - if (!google::protobuf::TextFormat::MergeFromString(ss.str(), &config)) - Error() << "couldn't load external config proto"; - return config; - } -} - -void FlagGroup::parseConfigFile(const std::string& filename, - const std::map& configFiles) -{ - auto newConfig = parseSingleConfigFile(filename, configFiles); - - /* The includes need to be merged _first_. */ - - for (const auto& include : newConfig.include()) - { - auto included = parseSingleConfigFile(include, configFiles); - if (included.include_size() != 0) - Error() << "only one level of config file includes are supported " - "(so far, if you need this, complain)"; - - config.MergeFrom(included); - } - - config.MergeFrom(newConfig); -} - -void FlagGroup::checkInitialised() const -{ - if (!_initialised) - throw std::runtime_error("Attempt to access uninitialised flag"); -} - -Flag::Flag(const std::vector& names, const std::string helptext): - _group(*currentFlagGroup), - _names(names), - _helptext(helptext) -{ - if (!currentFlagGroup) - Error() << "no flag group defined for " << *names.begin(); - _group.addFlag(this); -} - -void BoolFlag::set(const std::string& value) -{ - if ((value == "true") || (value == "y")) - _value = true; - else if ((value == "false") || (value == "n")) - _value = false; - else - Error() << "can't parse '" << value << "'; try 'true' or 'false'"; - _callback(_value); - _isSet = true; -} - -const std::string HexIntFlag::defaultValueAsString() const -{ - return fmt::format("0x{:x}", _defaultValue); -} - -static void doHelp() -{ - std::cout << "FluxEngine options:\n"; - std::cout - << "Note: options are processed left to right and order matters!\n"; - for (auto flag : all_flags) - { - std::cout << " "; - bool firstname = true; - for (auto name : flag->names()) - { - if (!firstname) - std::cout << ", "; - std::cout << name; - firstname = false; - } - - if (flag->hasArgument()) - std::cout << " defaultValueAsString() - << "\">"; - std::cout << ": " << flag->helptext() << std::endl; - } - exit(0); -} - -static void doShowConfig() -{ - std::string s; - google::protobuf::TextFormat::PrintToString(config, &s); - std::cout << s << '\n'; - - exit(0); -} - -static void doDoc() -{ - const auto fields = findAllProtoFields(&config); - for (const auto field : fields) - { - const std::string& path = field.first; - const google::protobuf::FieldDescriptor* f = field.second; - - if (f->type() == google::protobuf::FieldDescriptor::TYPE_MESSAGE) - continue; - - std::string helpText = f->options().GetExtension(help); - std::cout << fmt::format("{}: {}\n", path, helpText); - } - - exit(0); -} +#include "globals.h" +#include "flags.h" +#include "proto.h" +#include "utils.h" +#include "logger.h" +#include "fmt/format.h" +#include +#include +#include + +static FlagGroup* currentFlagGroup; +static std::vector all_flags; +static std::map flags_by_name; + +static void doHelp(); +static void doShowConfig(); +static void doDoc(); + +static FlagGroup helpGroup; +static ActionFlag helpFlag = ActionFlag({"--help"}, "Shows the help.", doHelp); + +static ActionFlag showConfigFlag = ActionFlag({"--config", "-C"}, + "Shows the currently set configuration and halts.", + doShowConfig); + +static ActionFlag docFlag = ActionFlag( + {"--doc"}, "Shows the available configuration options and halts.", doDoc); + +FlagGroup::FlagGroup() +{ + currentFlagGroup = this; +} + +FlagGroup::FlagGroup(std::initializer_list groups): _groups(groups) +{ + currentFlagGroup = this; +} + +void FlagGroup::addFlag(Flag* flag) +{ + _flags.push_back(flag); +} + +void FlagGroup::applyOption(const OptionProto& option) +{ + if (option.config().option_size() > 0) + Error() << fmt::format( + "option '{}' has an option inside it, which isn't " + "allowed", + option.name()); + if (option.config().option_group_size() > 0) + Error() << fmt::format( + "option '{}' has an option group inside it, which isn't " + "allowed", + option.name()); + if (option.config().include_size() > 0) + Error() << fmt::format( + "option '{}' is trying to include something, which " + "isn't allowed", + option.name()); + + Logger() << fmt::format("OPTION: {}", + option.has_message() ? option.message() : option.comment()); + + config.MergeFrom(option.config()); +} + +bool FlagGroup::applyOption(const std::string& optionName) +{ + auto searchOptionList = [&](auto& optionList) + { + for (const auto& option : optionList) + { + if (optionName == option.name()) + { + applyOption(option); + return true; + } + } + return false; + }; + + if (searchOptionList(config.option())) + return true; + + for (const auto& optionGroup : config.option_group()) + { + if (searchOptionList(optionGroup.option())) + return true; + } + + return false; +} + +std::vector FlagGroup::parseFlagsWithFilenames(int argc, + const char* argv[], + std::function callback) +{ + if (_initialised) + throw std::runtime_error("called parseFlags() twice"); + + /* Recursively accumulate a list of all flags. */ + + all_flags.clear(); + flags_by_name.clear(); + std::function recurse; + recurse = [&](FlagGroup* group) + { + if (group->_initialised) + return; + + for (FlagGroup* subgroup : group->_groups) + recurse(subgroup); + + for (Flag* flag : group->_flags) + { + for (const auto& name : flag->names()) + { + if (flags_by_name.find(name) != flags_by_name.end()) + Error() << "two flags use the name '" << name << "'"; + flags_by_name[name] = flag; + } + + all_flags.push_back(flag); + } + + group->_initialised = true; + }; + recurse(this); + recurse(&helpGroup); + + /* Now actually parse them. */ + + std::set options; + std::vector> overrides; + std::vector filenames; + int index = 1; + while (index < argc) + { + std::string thisarg = argv[index]; + std::string thatarg = (index < (argc - 1)) ? argv[index + 1] : ""; + + std::string key; + std::string value; + bool usesthat = false; + + if (thisarg.size() == 0) + { + /* Ignore this argument. */ + } + else if (thisarg[0] != '-') + { + /* This is a filename. Pass it to the callback, and if not consumed + * queue it. */ + if (!callback(thisarg)) + filenames.push_back(thisarg); + } + else + { + /* This is a flag. */ + + if ((thisarg.size() > 1) && (thisarg[1] == '-')) + { + /* Long option. */ + + auto equals = thisarg.rfind('='); + if (equals != std::string::npos) + { + key = thisarg.substr(0, equals); + value = thisarg.substr(equals + 1); + } + else + { + key = thisarg; + value = thatarg; + usesthat = true; + } + } + else + { + /* Short option. */ + + if (thisarg.size() > 2) + { + key = thisarg.substr(0, 2); + value = thisarg.substr(2); + } + else + { + key = thisarg; + value = thatarg; + usesthat = true; + } + } + + auto flag = flags_by_name.find(key); + if (flag == flags_by_name.end()) + { + if (beginsWith(key, "--")) + { + std::string path = key.substr(2); + if (key.find('.') != std::string::npos) + { + overrides.push_back(std::make_pair(path, value)); + index += usesthat; + } + else + options.insert(path); + } + else + Error() << "unrecognised flag; try --help"; + } + else + { + flag->second->set(value); + if (usesthat && flag->second->hasArgument()) + index++; + } + } + + index++; + } + + /* Apply any default options in groups. */ + + for (auto& group : config.option_group()) + { + const OptionProto* defaultOption = &*group.option().begin(); + bool isSet = false; + + for (auto& option : group.option()) + { + if (options.find(option.name()) != options.end()) + { + defaultOption = &option; + options.erase(option.name()); + } + } + + FlagGroup::applyOption(*defaultOption); + } + + /* Next, any standalone options. */ + + for (auto& option : config.option()) + { + if (options.find(option.name()) != options.end()) + { + FlagGroup::applyOption(option); + options.erase(option.name()); + } + } + + if (!options.empty()) + Error() << fmt::format( + "--{} is not a known flag or format option; try --help", + *options.begin()); + + /* Now apply any value overrides (in order). */ + + for (auto [k, v] : overrides) + { + ProtoField protoField = resolveProtoPath(&config, k); + setProtoFieldFromString(protoField, v); + } + + return filenames; +} + +void FlagGroup::parseFlags(int argc, + const char* argv[], + std::function callback) +{ + auto filenames = parseFlagsWithFilenames(argc, argv, callback); + if (!filenames.empty()) + Error() << "non-option parameter " << *filenames.begin() + << " seen (try --help)"; +} + +void FlagGroup::parseFlagsWithConfigFiles(int argc, + const char* argv[], + const std::map& configFiles) +{ + parseFlags(argc, + argv, + [&](const auto& filename) + { + parseConfigFile(filename, configFiles); + return true; + }); +} + +ConfigProto FlagGroup::parseSingleConfigFile(const std::string& filename, + const std::map& configFiles) +{ + const auto& it = configFiles.find(filename); + if (it != configFiles.end()) + { + ConfigProto config; + if (!config.ParseFromString(it->second)) + Error() << "couldn't load built-in config proto"; + return config; + } + else + { + std::ifstream f(filename, std::ios::out); + if (f.fail()) + Error() << fmt::format( + "Cannot open '{}': {}", filename, strerror(errno)); + + std::ostringstream ss; + ss << f.rdbuf(); + + ConfigProto config; + if (!google::protobuf::TextFormat::MergeFromString(ss.str(), &config)) + Error() << "couldn't load external config proto"; + return config; + } +} + +void FlagGroup::parseConfigFile(const std::string& filename, + const std::map& configFiles) +{ + auto newConfig = parseSingleConfigFile(filename, configFiles); + + /* The includes need to be merged _first_. */ + + for (const auto& include : newConfig.include()) + { + auto included = parseSingleConfigFile(include, configFiles); + if (included.include_size() != 0) + Error() << "only one level of config file includes are supported " + "(so far, if you need this, complain)"; + + config.MergeFrom(included); + } + + config.MergeFrom(newConfig); +} + +void FlagGroup::checkInitialised() const +{ + if (!_initialised) + throw std::runtime_error("Attempt to access uninitialised flag"); +} + +Flag::Flag(const std::vector& names, const std::string helptext): + _group(*currentFlagGroup), + _names(names), + _helptext(helptext) +{ + if (!currentFlagGroup) + Error() << "no flag group defined for " << *names.begin(); + _group.addFlag(this); +} + +void BoolFlag::set(const std::string& value) +{ + if ((value == "true") || (value == "y")) + _value = true; + else if ((value == "false") || (value == "n")) + _value = false; + else + Error() << "can't parse '" << value << "'; try 'true' or 'false'"; + _callback(_value); + _isSet = true; +} + +const std::string HexIntFlag::defaultValueAsString() const +{ + return fmt::format("0x{:x}", _defaultValue); +} + +static void doHelp() +{ + std::cout << "FluxEngine options:\n"; + std::cout + << "Note: options are processed left to right and order matters!\n"; + for (auto flag : all_flags) + { + std::cout << " "; + bool firstname = true; + for (auto name : flag->names()) + { + if (!firstname) + std::cout << ", "; + std::cout << name; + firstname = false; + } + + if (flag->hasArgument()) + std::cout << " defaultValueAsString() + << "\">"; + std::cout << ": " << flag->helptext() << std::endl; + } + exit(0); +} + +static void doShowConfig() +{ + std::string s; + google::protobuf::TextFormat::PrintToString(config, &s); + std::cout << s << '\n'; + + exit(0); +} + +static void doDoc() +{ + const auto fields = findAllProtoFields(&config); + for (const auto field : fields) + { + const std::string& path = field.first; + const google::protobuf::FieldDescriptor* f = field.second; + + if (f->type() == google::protobuf::FieldDescriptor::TYPE_MESSAGE) + continue; + + std::string helpText = f->options().GetExtension(help); + std::cout << fmt::format("{}: {}\n", path, helpText); + } + + exit(0); +} diff --git a/lib/flags.h b/lib/flags.h index a13f2e5a..90a67f5e 100644 --- a/lib/flags.h +++ b/lib/flags.h @@ -1,327 +1,329 @@ -#ifndef FLAGS_H -#define FLAGS_H - -class DataSpec; -class Flag; -class ConfigProto; - -class FlagGroup -{ -public: - FlagGroup(); - FlagGroup(std::initializer_list groups); - -public: - void parseFlags( - int argc, - const char* argv[], - std::function callback = - [](const auto&) - { - return false; - }); - std::vector parseFlagsWithFilenames( - int argc, - const char* argv[], - std::function callback = - [](const auto&) - { - return false; - }); - void parseFlagsWithConfigFiles(int argc, - const char* argv[], - const std::map& configFiles); - - /* Load one config file (or internal config file), without expanding - * includes. */ - - static ConfigProto parseSingleConfigFile(const std::string& filename, - const std::map& configFiles); - - /* Load a top-level config file (or internal config file), expanding - * includes. */ - - static void parseConfigFile(const std::string& filename, - const std::map& configFiles); - - /* Modify the current config to engage the named option. */ - - static bool applyOption(const std::string& option); - - void addFlag(Flag* flag); - void checkInitialised() const; - -private: - bool _initialised = false; - const std::vector _groups; - std::vector _flags; -}; - -class Flag -{ -public: - Flag(const std::vector& names, const std::string helptext); - virtual ~Flag(){}; - - void checkInitialised() const - { - _group.checkInitialised(); - } - - const std::string& name() const - { - return _names[0]; - } - const std::vector names() const - { - return _names; - } - const std::string& helptext() const - { - return _helptext; - } - - virtual bool hasArgument() const = 0; - virtual const std::string defaultValueAsString() const = 0; - virtual void set(const std::string& value) = 0; - -private: - FlagGroup& _group; - const std::vector _names; - const std::string _helptext; -}; - -class ActionFlag : Flag -{ -public: - ActionFlag(const std::vector& names, - const std::string helptext, - std::function callback): - Flag(names, helptext), - _callback(callback) - { - } - - bool hasArgument() const - { - return false; - } - const std::string defaultValueAsString() const - { - return ""; - } - void set(const std::string& value) - { - _callback(); - } - -private: - const std::function _callback; -}; - -class SettableFlag : public Flag -{ -public: - SettableFlag( - const std::vector& names, const std::string helptext): - Flag(names, helptext) - { - } - - operator bool() const - { - checkInitialised(); - return _value; - } - - bool hasArgument() const - { - return false; - } - const std::string defaultValueAsString() const - { - return "false"; - } - void set(const std::string& value) - { - _value = true; - } - -private: - bool _value = false; -}; - -template -class ValueFlag : public Flag -{ -public: - ValueFlag( - const std::vector& names, - const std::string helptext, - const T defaultValue, - std::function callback = - [](const T&) - { - }): - Flag(names, helptext), - _defaultValue(defaultValue), - _value(defaultValue), - _callback(callback) - { - } - - const T& get() const - { - checkInitialised(); - return _value; - } - - operator const T&() const - { - return get(); - } - - bool isSet() const - { - return _isSet; - } - - void setDefaultValue(T value) - { - _value = _defaultValue = value; - } - - bool hasArgument() const - { - return true; - } - -protected: - T _defaultValue; - T _value; - bool _isSet = false; - std::function _callback; -}; - -class StringFlag : public ValueFlag -{ -public: - StringFlag( - const std::vector& names, - const std::string helptext, - const std::string defaultValue = "", - std::function callback = - [](const std::string&) - { - }): - ValueFlag(names, helptext, defaultValue, callback) - { - } - - const std::string defaultValueAsString() const - { - return _defaultValue; - } - void set(const std::string& value) - { - _value = value; - _callback(_value); - _isSet = true; - } -}; - -class IntFlag : public ValueFlag -{ -public: - IntFlag( - const std::vector& names, - const std::string helptext, - int defaultValue = 0, - std::function callback = - [](const int&) - { - }): - ValueFlag(names, helptext, defaultValue, callback) - { - } - - const std::string defaultValueAsString() const - { - return std::to_string(_defaultValue); - } - void set(const std::string& value) - { - _value = std::stoi(value); - _callback(_value); - _isSet = true; - } -}; - -class HexIntFlag : public IntFlag -{ -public: - HexIntFlag( - const std::vector& names, - const std::string helptext, - int defaultValue = 0, - std::function callback = - [](const int&) - { - }): - IntFlag(names, helptext, defaultValue, callback) - { - } - - const std::string defaultValueAsString() const; -}; - -class DoubleFlag : public ValueFlag -{ -public: - DoubleFlag( - const std::vector& names, - const std::string helptext, - double defaultValue = 1.0, - std::function callback = - [](const double&) - { - }): - ValueFlag(names, helptext, defaultValue, callback) - { - } - - const std::string defaultValueAsString() const - { - return std::to_string(_defaultValue); - } - void set(const std::string& value) - { - _value = std::stod(value); - _callback(_value); - _isSet = true; - } -}; - -class BoolFlag : public ValueFlag -{ -public: - BoolFlag( - const std::vector& names, - const std::string helptext, - bool defaultValue = false, - std::function callback = - [](const bool&) - { - }): - ValueFlag(names, helptext, defaultValue, callback) - { - } - - const std::string defaultValueAsString() const - { - return _defaultValue ? "true" : "false"; - } - void set(const std::string& value); -}; - -#endif +#ifndef FLAGS_H +#define FLAGS_H + +class DataSpec; +class Flag; +class ConfigProto; +class OptionProto; + +class FlagGroup +{ +public: + FlagGroup(); + FlagGroup(std::initializer_list groups); + +public: + void parseFlags( + int argc, + const char* argv[], + std::function callback = + [](const auto&) + { + return false; + }); + std::vector parseFlagsWithFilenames( + int argc, + const char* argv[], + std::function callback = + [](const auto&) + { + return false; + }); + void parseFlagsWithConfigFiles(int argc, + const char* argv[], + const std::map& configFiles); + + /* Load one config file (or internal config file), without expanding + * includes. */ + + static ConfigProto parseSingleConfigFile(const std::string& filename, + const std::map& configFiles); + + /* Load a top-level config file (or internal config file), expanding + * includes. */ + + static void parseConfigFile(const std::string& filename, + const std::map& configFiles); + + /* Modify the current config to engage the named option. */ + + static void applyOption(const OptionProto& option); + static bool applyOption(const std::string& option); + + void addFlag(Flag* flag); + void checkInitialised() const; + +private: + bool _initialised = false; + const std::vector _groups; + std::vector _flags; +}; + +class Flag +{ +public: + Flag(const std::vector& names, const std::string helptext); + virtual ~Flag(){}; + + void checkInitialised() const + { + _group.checkInitialised(); + } + + const std::string& name() const + { + return _names[0]; + } + const std::vector names() const + { + return _names; + } + const std::string& helptext() const + { + return _helptext; + } + + virtual bool hasArgument() const = 0; + virtual const std::string defaultValueAsString() const = 0; + virtual void set(const std::string& value) = 0; + +private: + FlagGroup& _group; + const std::vector _names; + const std::string _helptext; +}; + +class ActionFlag : Flag +{ +public: + ActionFlag(const std::vector& names, + const std::string helptext, + std::function callback): + Flag(names, helptext), + _callback(callback) + { + } + + bool hasArgument() const + { + return false; + } + const std::string defaultValueAsString() const + { + return ""; + } + void set(const std::string& value) + { + _callback(); + } + +private: + const std::function _callback; +}; + +class SettableFlag : public Flag +{ +public: + SettableFlag( + const std::vector& names, const std::string helptext): + Flag(names, helptext) + { + } + + operator bool() const + { + checkInitialised(); + return _value; + } + + bool hasArgument() const + { + return false; + } + const std::string defaultValueAsString() const + { + return "false"; + } + void set(const std::string& value) + { + _value = true; + } + +private: + bool _value = false; +}; + +template +class ValueFlag : public Flag +{ +public: + ValueFlag( + const std::vector& names, + const std::string helptext, + const T defaultValue, + std::function callback = + [](const T&) + { + }): + Flag(names, helptext), + _defaultValue(defaultValue), + _value(defaultValue), + _callback(callback) + { + } + + const T& get() const + { + checkInitialised(); + return _value; + } + + operator const T&() const + { + return get(); + } + + bool isSet() const + { + return _isSet; + } + + void setDefaultValue(T value) + { + _value = _defaultValue = value; + } + + bool hasArgument() const + { + return true; + } + +protected: + T _defaultValue; + T _value; + bool _isSet = false; + std::function _callback; +}; + +class StringFlag : public ValueFlag +{ +public: + StringFlag( + const std::vector& names, + const std::string helptext, + const std::string defaultValue = "", + std::function callback = + [](const std::string&) + { + }): + ValueFlag(names, helptext, defaultValue, callback) + { + } + + const std::string defaultValueAsString() const + { + return _defaultValue; + } + void set(const std::string& value) + { + _value = value; + _callback(_value); + _isSet = true; + } +}; + +class IntFlag : public ValueFlag +{ +public: + IntFlag( + const std::vector& names, + const std::string helptext, + int defaultValue = 0, + std::function callback = + [](const int&) + { + }): + ValueFlag(names, helptext, defaultValue, callback) + { + } + + const std::string defaultValueAsString() const + { + return std::to_string(_defaultValue); + } + void set(const std::string& value) + { + _value = std::stoi(value); + _callback(_value); + _isSet = true; + } +}; + +class HexIntFlag : public IntFlag +{ +public: + HexIntFlag( + const std::vector& names, + const std::string helptext, + int defaultValue = 0, + std::function callback = + [](const int&) + { + }): + IntFlag(names, helptext, defaultValue, callback) + { + } + + const std::string defaultValueAsString() const; +}; + +class DoubleFlag : public ValueFlag +{ +public: + DoubleFlag( + const std::vector& names, + const std::string helptext, + double defaultValue = 1.0, + std::function callback = + [](const double&) + { + }): + ValueFlag(names, helptext, defaultValue, callback) + { + } + + const std::string defaultValueAsString() const + { + return std::to_string(_defaultValue); + } + void set(const std::string& value) + { + _value = std::stod(value); + _callback(_value); + _isSet = true; + } +}; + +class BoolFlag : public ValueFlag +{ +public: + BoolFlag( + const std::vector& names, + const std::string helptext, + bool defaultValue = false, + std::function callback = + [](const bool&) + { + }): + ValueFlag(names, helptext, defaultValue, callback) + { + } + + const std::string defaultValueAsString() const + { + return _defaultValue ? "true" : "false"; + } + void set(const std::string& value); +}; + +#endif diff --git a/lib/vfs/lif.cc b/lib/vfs/lif.cc new file mode 100644 index 00000000..fd86bdd2 --- /dev/null +++ b/lib/vfs/lif.cc @@ -0,0 +1,200 @@ +#include "lib/globals.h" +#include "lib/vfs/vfs.h" +#include "lib/config.pb.h" +#include "lib/utils.h" +#include + +/* See https://www.hp9845.net/9845/projects/hpdir/#lif_filesystem for + * a description. */ + +static void trimZeros(std::string s) +{ + s.erase(std::remove(s.begin(), s.end(), 0), s.end()); +} + +class LifFilesystem : public Filesystem +{ + class LifDirent : public Dirent + { + public: + LifDirent(const LifProto& config, Bytes& bytes) + { + file_type = TYPE_FILE; + + ByteReader br(bytes); + filename = trimWhitespace(br.read(10)); + uint16_t type = br.read_be16(); + location = br.read_be32(); + length = br.read_be32() * config.block_size(); + + mode = fmt::format("{:04x}", type); + path = {filename}; + + attributes[Filesystem::FILENAME] = filename; + attributes[Filesystem::LENGTH] = std::to_string(length); + attributes[Filesystem::FILE_TYPE] = "file"; + attributes[Filesystem::MODE] = mode; + } + + public: + uint32_t location; + }; + +public: + LifFilesystem( + const LifProto& config, std::shared_ptr sectors): + Filesystem(sectors), + _config(config) + { + } + + uint32_t capabilities() const + { + return OP_GETFSDATA | OP_LIST | OP_GETFILE | OP_GETDIRENT; + } + + FilesystemStatus check() override + { + return FS_OK; + } + + std::map getMetadata() override + { + mount(); + + std::map attributes; + + attributes[VOLUME_NAME] = _volumeLabel; + attributes[TOTAL_BLOCKS] = std::to_string(_totalBlocks); + attributes[USED_BLOCKS] = std::to_string(_usedBlocks); + attributes[BLOCK_SIZE] = std::to_string(_config.block_size()); + attributes["lif.directory_block"] = std::to_string(_directoryBlock); + attributes["lif.directory_size"] = std::to_string(_directorySize); + return attributes; + } + + std::shared_ptr getDirent(const Path& path) override + { + mount(); + if (path.size() != 1) + throw BadPathException(); + + return findFile(path.front()); + } + + std::vector> list(const Path& path) override + { + mount(); + if (!path.empty()) + throw FileNotFoundException(); + + std::vector> result; + for (auto& de : _dirents) + result.push_back(de); + return result; + } + + Bytes getFile(const Path& path) override + { + mount(); + if (path.size() != 1) + throw BadPathException(); + + auto dirent = findFile(path.front()); + + return getLifBlock( + dirent->location, dirent->length / _config.block_size()); + } + +private: + void mount() + { + _sectorSize = getLogicalSectorSize(); + _sectorsPerBlock = _sectorSize / _config.block_size(); + + _rootBlock = getLifBlock(0); + + ByteReader rbr(_rootBlock); + if (rbr.read_be16() != 0x8000) + throw BadFilesystemException(); + _volumeLabel = trimWhitespace(rbr.read(6)); + _directoryBlock = rbr.read_be32(); + rbr.skip(4); + _directorySize = rbr.read_be32(); + unsigned tracks = rbr.read_be32(); + unsigned heads = rbr.read_be32(); + unsigned sectors = rbr.read_be32(); + _usedBlocks = 1 + _directorySize; + + Bytes directory = getLifBlock(_directoryBlock, _directorySize); + + _dirents.clear(); + ByteReader br(directory); + while (!br.eof()) + { + Bytes direntBytes = br.read(32); + if (direntBytes[0] != 0xff) + { + auto dirent = std::make_unique(_config, direntBytes); + _usedBlocks += dirent->length / _config.block_size(); + _dirents.push_back(std::move(dirent)); + } + } + _totalBlocks = std::max(tracks * heads * sectors, _usedBlocks); + } + + std::shared_ptr findFile(const std::string filename) + { + for (const auto& dirent : _dirents) + { + if (dirent->filename == filename) + return dirent; + } + + throw FileNotFoundException(); + } + + Bytes getLifBlock(uint32_t number, uint32_t count) + { + Bytes b; + ByteWriter bw(b); + + while (count) + { + bw += getLifBlock(number); + number++; + count--; + } + + return b; + } + + Bytes getLifBlock(uint32_t number) + { + /* LIF uses 256-byte blocks, but the underlying format can have much + * bigger sectors. */ + + Bytes sector = getLogicalSector(number / _sectorsPerBlock); + unsigned offset = number % _sectorsPerBlock; + return sector.slice( + offset * _config.block_size(), _config.block_size()); + } + +private: + const LifProto& _config; + int _sectorSize; + int _sectorsPerBlock; + Bytes _rootBlock; + std::string _volumeLabel; + unsigned _directoryBlock; + unsigned _directorySize; + unsigned _totalBlocks; + unsigned _usedBlocks; + std::vector> _dirents; +}; + +std::unique_ptr Filesystem::createLifFilesystem( + const FilesystemProto& config, std::shared_ptr sectors) +{ + return std::make_unique(config.lif(), sectors); +} diff --git a/lib/vfs/philefs.cc b/lib/vfs/philefs.cc index 6038f5a4..5842f810 100644 --- a/lib/vfs/philefs.cc +++ b/lib/vfs/philefs.cc @@ -233,8 +233,8 @@ private: _rootBlock = getPsosBlock(2, 1); _bitmapBlockNumber = _rootBlock.reader().seek(0x1c).read_be16(); _filedesBlockNumber = _rootBlock.reader().seek(0x1e).read_be16(); - _filedesLength = - _rootBlock.reader().seek(0x20).read_be16() - _filedesBlockNumber + 1; + _filedesLength = _rootBlock.reader().seek(0x20).read_be16() - + _filedesBlockNumber + 1; _totalBlocks = _rootBlock.reader().seek(0x18).read_be16(); Bytes directoryBlock = getPsosBlock(3, 1); diff --git a/lib/vfs/vfs.cc b/lib/vfs/vfs.cc index 72128a73..6eefe4ea 100644 --- a/lib/vfs/vfs.cc +++ b/lib/vfs/vfs.cc @@ -212,6 +212,9 @@ std::unique_ptr Filesystem::createFilesystem( case FilesystemProto::PHILE: return Filesystem::createPhileFilesystem(config, image); + case FilesystemProto::LIF: + return Filesystem::createLifFilesystem(config, image); + default: Error() << "no filesystem configured"; return std::unique_ptr(); diff --git a/lib/vfs/vfs.h b/lib/vfs/vfs.h index 857aa7bf..41e6ae2f 100644 --- a/lib/vfs/vfs.h +++ b/lib/vfs/vfs.h @@ -254,6 +254,8 @@ public: const FilesystemProto& config, std::shared_ptr image); static std::unique_ptr createPhileFilesystem( const FilesystemProto& config, std::shared_ptr image); + static std::unique_ptr createLifFilesystem( + const FilesystemProto& config, std::shared_ptr image); static std::unique_ptr createFilesystem( const FilesystemProto& config, std::shared_ptr image); diff --git a/lib/vfs/vfs.proto b/lib/vfs/vfs.proto index 22bf997f..0c7630df 100644 --- a/lib/vfs/vfs.proto +++ b/lib/vfs/vfs.proto @@ -59,26 +59,33 @@ message CbmfsProto message ProdosProto {} -message AppledosProto { - optional uint32 filesystem_offset_sectors = 1 [ - default = 0, - (help) = "offset the entire offset up the disk this many sectors" - ]; +message AppledosProto +{ + optional uint32 filesystem_offset_sectors = 1 [ + default = 0, + (help) = "offset the entire offset up the disk this many sectors" + ]; } message Smaky6FsProto {} -message PhileProto { - optional uint32 block_size = 1 [ - default = 1024, - (help) = "Phile filesystem block size" - ]; +message PhileProto +{ + optional uint32 block_size = 1 + [ default = 1024, (help) = "Phile filesystem block size" ]; } -// NEXT_TAG: 14 +message LifProto +{ + optional uint32 block_size = 1 + [ default = 256, (help) = "LIF filesystem block size" ]; +} + +// NEXT_TAG: 15 message FilesystemProto { - enum FilesystemType { + enum FilesystemType + { NOT_SET = 0; ACORNDFS = 1; BROTHER120 = 2; @@ -88,12 +95,14 @@ message FilesystemProto MACHFS = 6; CBMFS = 7; PRODOS = 8; - SMAKY6 = 9; - APPLEDOS = 10; - PHILE = 11; + SMAKY6 = 9; + APPLEDOS = 10; + PHILE = 11; + LIF = 12; } - optional FilesystemType type = 10 [default = NOT_SET, (help) = "filesystem type"]; + optional FilesystemType type = 10 + [ default = NOT_SET, (help) = "filesystem type" ]; optional AcornDfsProto acorndfs = 1; optional Brother120FsProto brother120 = 2; @@ -103,9 +112,11 @@ message FilesystemProto optional MacHfsProto machfs = 6; optional CbmfsProto cbmfs = 7; optional ProdosProto prodos = 8; - optional AppledosProto appledos = 12; - optional Smaky6FsProto smaky6 = 11; - optional PhileProto phile = 13; - - optional SectorListProto sector_order = 9 [(help) = "specify the filesystem order of sectors"]; + optional AppledosProto appledos = 12; + optional Smaky6FsProto smaky6 = 11; + optional PhileProto phile = 13; + optional LifProto lif = 14; + + optional SectorListProto sector_order = 9 + [ (help) = "specify the filesystem order of sectors" ]; } diff --git a/src/formats/_acornadfs32.textpb b/src/formats/_acornadfs32.textpb deleted file mode 100644 index 11a65cf8..00000000 --- a/src/formats/_acornadfs32.textpb +++ /dev/null @@ -1,26 +0,0 @@ -comment: 'Acorn ADFS generic settings (32-bit formats)' -is_extension: true - -image_writer { - filename: "acornadfs.img" - type: IMG -} - -layout { - tracks: 80 - sides: 2 - layoutdata { - sector_size: 1024 - physical { - start_sector: 0 - } - } -} - -decoder { - ibm { - trackdata { - ignore_side_byte: true - } - } -} diff --git a/src/formats/_acornadfs8.textpb b/src/formats/_acornadfs8.textpb deleted file mode 100644 index 807a162c..00000000 --- a/src/formats/_acornadfs8.textpb +++ /dev/null @@ -1,25 +0,0 @@ -comment: 'Acorn ADFS generic settings (8-bit formats)' -is_extension: true - -image_writer { - filename: "acornadfs.img" - type: IMG -} - -layout { - layoutdata { - sector_size: 256 - physical { - start_sector: 0 - count: 16 - } - } -} - -decoder { - ibm { - trackdata { - ignore_side_byte: true - } - } -} diff --git a/src/formats/_apple2.textpb b/src/formats/_apple2.textpb deleted file mode 100644 index 7e92f263..00000000 --- a/src/formats/_apple2.textpb +++ /dev/null @@ -1,202 +0,0 @@ -comment: 'Apple II generic settings' -is_extension: true - -drive { - high_density: false -} - -decoder { - apple2 {} -} - -encoder { - apple2 {} -} - -option { - name: "nofs" - comment: "use physical CHS sector order and no file system" - exclusivity_group: "format" -} - -option { - name: "appledos" - comment: "use AppleDOS soft sector skew and file system" - message: "compensating for AppleDOS soft sector skew" - exclusivity_group: "format" - - config { - image_reader { - filesystem_sector_order: true - } - - image_writer { - filesystem_sector_order: true - } - - filesystem { - type: APPLEDOS - } - - layout { - layoutdata { - filesystem { - sector: 0 - sector: 13 - sector: 11 - sector: 9 - sector: 7 - sector: 5 - sector: 3 - sector: 1 - sector: 14 - sector: 12 - sector: 10 - sector: 8 - sector: 6 - sector: 4 - sector: 2 - sector: 15 - } - } - } - } -} - -option { - name: "prodos" - comment: "use ProDOS soft sector skew and filesystem" - message: "compensating for ProDOS soft sector skew" - exclusivity_group: "format" - - config { - image_reader { - filesystem_sector_order: true - } - - image_writer { - filesystem_sector_order: true - } - - filesystem { - type: PRODOS - } - - layout { - layoutdata { - filesystem { - sector: 0 - sector: 2 - sector: 4 - sector: 6 - sector: 8 - sector: 10 - sector: 12 - sector: 14 - sector: 1 - sector: 3 - sector: 5 - sector: 7 - sector: 9 - sector: 11 - sector: 13 - sector: 15 - } - } - } - } -} - -option { - name: "cpm" - comment: "use CP/M soft sector skew and filesystem" - message: "compensating for CP/M soft sector skew" - exclusivity_group: "format" - - config { - image_reader { - filesystem_sector_order: true - } - - image_writer { - filesystem_sector_order: true - } - - filesystem { - type: CPMFS - cpmfs { - filesystem_start { - track: 3 - } - block_size: 4096 - dir_entries: 128 - } - } - - decoder { - apple2 { - side_one_track_offset: 80 - } - } - - encoder { - apple2 { - side_one_track_offset: 80 - } - } - - layout { - layoutdata { - # The boot tracks use ProDOS translation. - - track: 0 - up_to_track: 2 - filesystem { - sector: 0 - sector: 2 - sector: 4 - sector: 6 - sector: 8 - sector: 10 - sector: 12 - sector: 14 - sector: 1 - sector: 3 - sector: 5 - sector: 7 - sector: 9 - sector: 11 - sector: 13 - sector: 15 - } - } - - layoutdata { - # The data tracks use their own, special translation. - - track: 3 - up_to_track: 79 - filesystem { - sector: 0 - sector: 3 - sector: 6 - sector: 9 - sector: 12 - sector: 15 - sector: 2 - sector: 5 - sector: 8 - sector: 11 - sector: 14 - sector: 1 - sector: 4 - sector: 7 - sector: 10 - sector: 13 - } - } - } - } -} - - diff --git a/src/formats/_atari.textpb b/src/formats/_atari.textpb deleted file mode 100644 index ba192bdf..00000000 --- a/src/formats/_atari.textpb +++ /dev/null @@ -1,23 +0,0 @@ -comment: 'Common Atari definitions' -is_extension: true - -encoder { - ibm { - trackdata { - target_rotational_period_ms: 200 - target_clock_period_us: 4 - emit_iam: false - gap0: 80 - gap2: 22 - gap3: 34 - } - } -} - -decoder { - ibm { - trackdata { - ignore_sector: 66 - } - } -} diff --git a/src/formats/_micropolis.textpb b/src/formats/_micropolis.textpb deleted file mode 100644 index ba945f29..00000000 --- a/src/formats/_micropolis.textpb +++ /dev/null @@ -1,64 +0,0 @@ -comment: 'Common Micropolis definitions' -is_extension: true - -drive { - hard_sector_count: 16 -} - -image_reader { - filename: "micropolis.img" - type: IMG -} - -image_writer { - filename: "micropolis.img" - type: IMG -} - -layout { - layoutdata { - sector_size: 256 - physical { - start_sector: 0 - count: 16 - } - } -} - -encoder { - micropolis {} -} - -decoder { - micropolis {} -} - -option { - name: "vgi" - comment: "Read/write VGI format images" - message: "using VGI format" - - config { - image_reader { - filename: "micropolis.vgi" - type: IMG - } - - image_writer { - filename: "micropolis.vgi" - type: IMG - } - - layout { - layoutdata { - sector_size: 275 - } - } - - decoder { - micropolis { - sector_output_size: 275 - } - } - } -} diff --git a/src/formats/_mx.textpb b/src/formats/_mx.textpb deleted file mode 100644 index 20b269da..00000000 --- a/src/formats/_mx.textpb +++ /dev/null @@ -1,22 +0,0 @@ -comment: 'DVK MX common settings' -is_extension: true - -image_writer { - filename: "mx.img" - type: IMG -} - -layout { - layoutdata { - sector_size: 256 - physical { - start_sector: 0 - count: 11 - } - } -} - -decoder { - mx {} -} - diff --git a/src/formats/_northstar.textpb b/src/formats/_northstar.textpb deleted file mode 100644 index f6adedf0..00000000 --- a/src/formats/_northstar.textpb +++ /dev/null @@ -1,35 +0,0 @@ -comment: 'Northstar generic settings' -is_extension: true - -image_reader { - filename: "northstar.nsi" - type: NSI -} - -image_writer { - filename: "northstar.nsi" - type: NSI -} - -layout { - layoutdata { - physical { - start_sector: 0 - count: 10 - } - } -} - -drive { - hard_sector_count: 10 - sync_with_index: true -} - -encoder { - northstar {} -} - -decoder { - northstar {} -} - diff --git a/src/formats/acornadfs.textpb b/src/formats/acornadfs.textpb new file mode 100644 index 00000000..e70406c5 --- /dev/null +++ b/src/formats/acornadfs.textpb @@ -0,0 +1,121 @@ +comment: 'Acorn ADFS family (ro)' + +image_writer { + filename: "acornadfs.img" + type: IMG +} + +decoder { + ibm { + trackdata { + ignore_side_byte: true + } + } +} + +option_group { + comment: "Format variant" + + option { + name: "160" + comment: '160kB 3.5" or 5.25" 40-track SSDD; S format' + + config { + tpi: 48 + layout { + tracks: 40 + sides: 1 + layoutdata { + sector_size: 256 + physical { + start_sector: 0 + count: 16 + } + } + } + } + } + + option { + name: "320" + comment: '320kB 3.5" or 5.25" 80-track SSDD; M format' + + config { + tpi: 96 + layout { + tracks: 80 + sides: 1 + layoutdata { + sector_size: 256 + physical { + start_sector: 0 + count: 16 + } + } + } + } + } + + option { + name: "640" + comment: '640kB 3.5" or 5.25" 80-track DSDD; L format' + + config { + tpi: 96 + layout { + tracks: 80 + sides: 2 + layoutdata { + sector_size: 256 + physical { + start_sector: 0 + count: 16 + } + } + } + } + } + + option { + name: "800" + comment: '800kB 3.5" 80-track DSDD; D and E formats' + + config { + tpi: 96 + layout { + tracks: 80 + sides: 2 + layoutdata { + sector_size: 1024 + physical { + start_sector: 0 + count: 5 + } + } + } + } + } + + option { + name: "1600" + comment: '1600kB 3.5" 80-track DSHD; F formats' + set_by_default: true + + config { + tpi: 96 + layout { + tracks: 80 + sides: 2 + layoutdata { + sector_size: 1024 + physical { + start_sector: 0 + count: 10 + } + } + } + } + } +} + + diff --git a/src/formats/acornadfs160.textpb b/src/formats/acornadfs160.textpb deleted file mode 100644 index f83ebcc8..00000000 --- a/src/formats/acornadfs160.textpb +++ /dev/null @@ -1,9 +0,0 @@ -comment: 'Acorn ADFS S-format 160kB 3.5" or 5.25" 40-track SS' -include: '_acornadfs8' - -layout { - tracks: 40 - sides: 1 -} - -tpi: 48 diff --git a/src/formats/acornadfs1600.textpb b/src/formats/acornadfs1600.textpb deleted file mode 100644 index b8efe971..00000000 --- a/src/formats/acornadfs1600.textpb +++ /dev/null @@ -1,10 +0,0 @@ -comment: 'Acorn ADFS F-format 1600kB 3.5" 80-track DS' -include: '_acornadfs32' - -layout { - layoutdata { - physical { - count: 10 - } - } -} diff --git a/src/formats/acornadfs320.textpb b/src/formats/acornadfs320.textpb deleted file mode 100644 index be58d674..00000000 --- a/src/formats/acornadfs320.textpb +++ /dev/null @@ -1,7 +0,0 @@ -comment: 'Acorn ADFS M-format 320kB 3.5" or 5.25" 80-track SS' -include: '_acornadfs8' - -layout { - tracks: 80 - sides: 1 -} diff --git a/src/formats/acornadfs640.textpb b/src/formats/acornadfs640.textpb deleted file mode 100644 index a6a2ccd9..00000000 --- a/src/formats/acornadfs640.textpb +++ /dev/null @@ -1,7 +0,0 @@ -comment: 'Acorn ADFS L-format 640kB 3.5" or 5.25" 80-track DS' -include: '_acornadfs8' - -layout { - tracks: 80 - sides: 2 -} diff --git a/src/formats/acornadfs800.textpb b/src/formats/acornadfs800.textpb deleted file mode 100644 index 474bd52b..00000000 --- a/src/formats/acornadfs800.textpb +++ /dev/null @@ -1,10 +0,0 @@ -comment: 'Acorn ADFS D and E-format 800kB 3.5" 80-track DS' -include: '_acornadfs32' - -layout { - layoutdata { - physical { - count: 5 - } - } -} diff --git a/src/formats/acorndfs.textpb b/src/formats/acorndfs.textpb index 5fd1085e..ecb665f4 100644 --- a/src/formats/acorndfs.textpb +++ b/src/formats/acorndfs.textpb @@ -1,4 +1,4 @@ -comment: 'Acorn DFS 100kB/200kB 3.5" or 5.25" 40- or 80-track SS (ro)' +comment: 'Acorn DFS fmaily' image_reader { filename: "acorndfs.img" @@ -11,7 +11,6 @@ image_writer { } layout { - tracks: 80 sides: 1 layoutdata { sector_size: 256 @@ -47,3 +46,30 @@ filesystem { type: ACORNDFS } +option_group { + comment: "Format variant" + + option { + name: "100" + comment: '100kB 40-track SSSD' + + config { + layout { + tracks: 40 + } + } + } + + option { + name: "200" + comment: '200kB 80-track SSSD' + set_by_default: true + + config { + layout { + tracks: 80 + } + } + } +} + diff --git a/src/formats/ampro.textpb b/src/formats/ampro.textpb new file mode 100644 index 00000000..800bb06d --- /dev/null +++ b/src/formats/ampro.textpb @@ -0,0 +1,58 @@ +comment: 'Ampro 5.25" family' + +image_writer { + filename: "ampro.img" + type: IMG +} + +layout { + sides: 2 + layoutdata { + sector_size: 1024 + physical { + start_sector: 17 + count: 5 + } + } +} + +decoder { + ibm {} +} + +filesystem { + type: CPMFS + cpmfs { + filesystem_start { + track: 1 + } + block_size: 2048 + dir_entries: 128 + } +} + +option_group { + comment: "Format variant" + + option { + name: "400" + comment: "400kB 40-track DSDD" + + config { + layout { + tracks: 40 + } + } + } + + option { + name: "800" + comment: "800kB 80-track DSDD" + + config { + layout { + tracks: 80 + } + } + } +} diff --git a/src/formats/ampro400.textpb b/src/formats/ampro400.textpb deleted file mode 100644 index 22447bd1..00000000 --- a/src/formats/ampro400.textpb +++ /dev/null @@ -1,22 +0,0 @@ -comment: 'Ampro 400kB/800kB 5.25" 40/80 track SSDD/DSDD (ro)' - -image_writer { - filename: "ampro.img" - type: IMG -} - -layout { - tracks: 80 - sides: 1 - layoutdata { - sector_size: 1024 - physical { - start_sector: 17 - count: 5 - } - } -} - -decoder { - ibm {} -} diff --git a/src/formats/ampro800.textpb b/src/formats/ampro800.textpb deleted file mode 100644 index 5d18cd40..00000000 --- a/src/formats/ampro800.textpb +++ /dev/null @@ -1,22 +0,0 @@ -comment: 'Ampro 400kB/800kB 5.25" 40/80 track SSDD/DSDD (ro)' - -image_writer { - filename: "ampro.img" - type: IMG -} - -layout { - tracks: 80 - sides: 2 - layoutdata { - sector_size: 1024 - physical { - start_sector: 17 - count: 5 - } - } -} - -decoder { - ibm {} -} diff --git a/src/formats/apple2.textpb b/src/formats/apple2.textpb new file mode 100644 index 00000000..637ffd23 --- /dev/null +++ b/src/formats/apple2.textpb @@ -0,0 +1,254 @@ +comment: 'Apple II family' + +decoder { + apple2 {} +} + +encoder { + apple2 {} +} + + +option_group { + comment: "Format variant" + + option { + name: "140" + comment: '140kB 5.25" 35-track SS' + set_by_default: true + + config { + layout { + tracks: 35 + sides: 1 + layoutdata { + sector_size: 256 + physical { + start_sector: 0 + count: 16 + } + } + } + tpi: 48 + } + } + + option { + name: "640" + comment: '640kB 5.25" 80-track DS' + + config { + layout { + tracks: 80 + sides: 2 + order: HCS + layoutdata { + sector_size: 256 + physical { + start_sector: 0 + count: 16 + } + } + } + tpi: 96 + } + } +} + +option { + name: "side1" + comment: "for AppleDOS file system access, read the volume on side 1 of a disk" + + config { + filesystem { + appledos { + filesystem_offset_sectors: 0x500 + } + } + } +} + +option_group { + comment: "Filesystem and sector skew" + + option { + name: "nofs" + comment: "use physical CHS sector order and no file system" + } + + option { + name: "appledos" + comment: "use AppleDOS soft sector skew and file system" + + config { + image_reader { + filesystem_sector_order: true + } + + image_writer { + filesystem_sector_order: true + } + + filesystem { + type: APPLEDOS + } + + layout { + layoutdata { + filesystem { + sector: 0 + sector: 13 + sector: 11 + sector: 9 + sector: 7 + sector: 5 + sector: 3 + sector: 1 + sector: 14 + sector: 12 + sector: 10 + sector: 8 + sector: 6 + sector: 4 + sector: 2 + sector: 15 + } + } + } + } + } + + option { + name: "prodos" + comment: "use ProDOS soft sector skew and filesystem" + set_by_default: true + + config { + image_reader { + filesystem_sector_order: true + } + + image_writer { + filesystem_sector_order: true + } + + filesystem { + type: PRODOS + } + + layout { + layoutdata { + filesystem { + sector: 0 + sector: 2 + sector: 4 + sector: 6 + sector: 8 + sector: 10 + sector: 12 + sector: 14 + sector: 1 + sector: 3 + sector: 5 + sector: 7 + sector: 9 + sector: 11 + sector: 13 + sector: 15 + } + } + } + } + } + + option { + name: "cpm" + comment: "use CP/M soft sector skew and filesystem" + + config { + image_reader { + filesystem_sector_order: true + } + + image_writer { + filesystem_sector_order: true + } + + filesystem { + type: CPMFS + cpmfs { + filesystem_start { + track: 3 + } + block_size: 4096 + dir_entries: 128 + } + } + + decoder { + apple2 { + side_one_track_offset: 80 + } + } + + encoder { + apple2 { + side_one_track_offset: 80 + } + } + + layout { + layoutdata { + # The boot tracks use ProDOS translation. + + track: 0 + up_to_track: 2 + filesystem { + sector: 0 + sector: 2 + sector: 4 + sector: 6 + sector: 8 + sector: 10 + sector: 12 + sector: 14 + sector: 1 + sector: 3 + sector: 5 + sector: 7 + sector: 9 + sector: 11 + sector: 13 + sector: 15 + } + } + + layoutdata { + # The data tracks use their own, special translation. + + track: 3 + up_to_track: 79 + filesystem { + sector: 0 + sector: 3 + sector: 6 + sector: 9 + sector: 12 + sector: 15 + sector: 2 + sector: 5 + sector: 8 + sector: 11 + sector: 14 + sector: 1 + sector: 4 + sector: 7 + sector: 10 + sector: 13 + } + } + } + } + } +} + diff --git a/src/formats/appleii140.textpb b/src/formats/appleii140.textpb deleted file mode 100644 index a725aeff..00000000 --- a/src/formats/appleii140.textpb +++ /dev/null @@ -1,27 +0,0 @@ -comment: 'Apple II 140kB 5.25" 35 track SSDD' -include: '_apple2' - -image_reader { - filename: "appleii140.img" - type: IMG -} - -image_writer { - filename: "appleii140.img" - type: IMG -} - -layout { - tracks: 35 - sides: 1 - layoutdata { - sector_size: 256 - physical { - start_sector: 0 - count: 16 - } - } -} - -tpi: 48 - diff --git a/src/formats/appleii640.textpb b/src/formats/appleii640.textpb deleted file mode 100644 index 3f472416..00000000 --- a/src/formats/appleii640.textpb +++ /dev/null @@ -1,42 +0,0 @@ -comment: 'Apple II 640kB 5.25" 80 track DSDD' -include: '_apple2' - -image_reader { - filename: "appleii640.img" - type: IMG -} - -image_writer { - filename: "appleii640.img" - type: IMG -} - -layout { - tracks: 80 - sides: 2 - order: HCS - layoutdata { - sector_size: 256 - physical { - start_sector: 0 - count: 16 - } - } -} - -tpi: 96 - -option { - name: "side1" - comment: "read the volume on side 1 of a disk (AppleDOS only)" - message: "accessing volume on side 1" - - config { - filesystem { - appledos { - filesystem_offset_sectors: 0x500 - } - } - } -} - diff --git a/src/formats/atarist.textpb b/src/formats/atarist.textpb new file mode 100644 index 00000000..9c6cdd7c --- /dev/null +++ b/src/formats/atarist.textpb @@ -0,0 +1,180 @@ +comment: 'Atari ST family' + +encoder { + ibm { + trackdata { + target_rotational_period_ms: 200 + target_clock_period_us: 4 + emit_iam: false + gap0: 80 + gap2: 22 + gap3: 34 + } + } +} + +decoder { + ibm { + trackdata { + ignore_sector: 66 + } + } +} + +option_group { + comment: "Format variant" + + option { + name: "360" + comment: '360kB 3.5" 80-track 9-sector SSDD' + + config { + layout { + tracks: 80 + sides: 1 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 9 + } + } + } + } + } + + option { + name: "370" + comment: '370kB 3.5" 82-track 9-sector SSDD' + + config { + layout { + tracks: 82 + sides: 1 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 9 + } + } + } + } + } + + option { + name: "400" + comment: '400kB 3.5" 80-track 10-sector SSDD' + + config { + layout { + tracks: 80 + sides: 1 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 10 + } + } + } + } + } + + option { + name: "410" + comment: '410kB 3.5" 82-track 10-sector SSDD' + + config { + layout { + tracks: 82 + sides: 1 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 10 + } + } + } + } + } + + option { + name: "720" + comment: '720kB 3.5" 80-track 9-sector DSDD' + set_by_default: true + + config { + layout { + tracks: 80 + sides: 2 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 9 + } + } + } + } + } + + option { + name: "740" + comment: '740kB 3.5" 82-track 9-sector DSDD' + + config { + layout { + tracks: 82 + sides: 2 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 9 + } + } + } + } + } + + option { + name: "800" + comment: '800kB 3.5" 80-track 10-sector DSDD' + + config { + layout { + tracks: 80 + sides: 2 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 10 + } + } + } + } + } + + option { + name: "820" + comment: '820kB 3.5" 82-track 10-sector DSDD' + + config { + layout { + tracks: 82 + sides: 2 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 10 + } + } + } + } + } +} + diff --git a/src/formats/atarist360.textpb b/src/formats/atarist360.textpb deleted file mode 100644 index efa26b06..00000000 --- a/src/formats/atarist360.textpb +++ /dev/null @@ -1,25 +0,0 @@ -comment: 'Atari ST 360kB 3.5" 80-track 9-sector SSDD' - -include: '_atari' - -image_reader { - filename: "atarist360.st" - type: IMG -} - -image_writer { - filename: "atarist360.st" - type: IMG -} - -layout { - tracks: 80 - sides: 1 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 9 - } - } -} diff --git a/src/formats/atarist370.textpb b/src/formats/atarist370.textpb deleted file mode 100644 index e93d56f5..00000000 --- a/src/formats/atarist370.textpb +++ /dev/null @@ -1,25 +0,0 @@ -comment: 'Atari ST 370kB 3.5" 82-track 9-sector SSDD' - -include: '_atari' - -image_reader { - filename: "atarist370.st" - type: IMG -} - -image_writer { - filename: "atarist370.st" - type: IMG -} - -layout { - tracks: 82 - sides: 1 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 9 - } - } -} diff --git a/src/formats/atarist400.textpb b/src/formats/atarist400.textpb deleted file mode 100644 index 92fd8fa6..00000000 --- a/src/formats/atarist400.textpb +++ /dev/null @@ -1,25 +0,0 @@ -comment: 'Atari ST 400kB 3.5" 80-track 10-sector SSDD' - -include: '_atari' - -image_reader { - filename: "atarist400.st" - type: IMG -} - -image_writer { - filename: "atarist400.st" - type: IMG -} - -layout { - tracks: 80 - sides: 1 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 10 - } - } -} diff --git a/src/formats/atarist410.textpb b/src/formats/atarist410.textpb deleted file mode 100644 index 22e82181..00000000 --- a/src/formats/atarist410.textpb +++ /dev/null @@ -1,25 +0,0 @@ -comment: 'Atari ST 410kB 3.5" 82-track 10-sector SSDD' - -include: '_atari' - -image_reader { - filename: "atarist410.st" - type: IMG -} - -image_writer { - filename: "atarist410.st" - type: IMG -} - -layout { - tracks: 82 - sides: 1 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 10 - } - } -} diff --git a/src/formats/atarist720.textpb b/src/formats/atarist720.textpb deleted file mode 100644 index c33a1a95..00000000 --- a/src/formats/atarist720.textpb +++ /dev/null @@ -1,25 +0,0 @@ -comment: 'Atari ST 720kB 3.5" 80-track 9-sector DSDD' - -include: '_atari' - -image_reader { - filename: "atarist720.st" - type: IMG -} - -image_writer { - filename: "atarist720.st" - type: IMG -} - -layout { - tracks: 80 - sides: 2 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 9 - } - } -} diff --git a/src/formats/atarist740.textpb b/src/formats/atarist740.textpb deleted file mode 100644 index daf04d90..00000000 --- a/src/formats/atarist740.textpb +++ /dev/null @@ -1,25 +0,0 @@ -comment: 'Atari ST 740kB 3.5" 82-track 9-sector DSDD' - -include: '_atari' - -image_reader { - filename: "atarist740.st" - type: IMG -} - -image_writer { - filename: "atarist740.st" - type: IMG -} - -layout { - tracks: 82 - sides: 2 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 9 - } - } -} diff --git a/src/formats/atarist800.textpb b/src/formats/atarist800.textpb deleted file mode 100644 index cf7d3f68..00000000 --- a/src/formats/atarist800.textpb +++ /dev/null @@ -1,26 +0,0 @@ -comment: 'Atari ST 800kB 3.5" 80-track 10-sector DSDD' - -include: '_atari' - -image_reader { - filename: "atarist800.st" - type: IMG -} - -image_writer { - filename: "atarist800.st" - type: IMG -} - -layout { - tracks: 80 - sides: 2 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 10 - } - } -} - diff --git a/src/formats/atarist820.textpb b/src/formats/atarist820.textpb deleted file mode 100644 index d5c4f678..00000000 --- a/src/formats/atarist820.textpb +++ /dev/null @@ -1,26 +0,0 @@ -comment: 'Atari ST 820kB 3.5" 82-track 10-sector DSDD' - -include: '_atari' - -image_reader { - filename: "atarist820.st" - type: IMG -} - -image_writer { - filename: "atarist820.st" - type: IMG -} - -layout { - tracks: 82 - sides: 2 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 10 - } - } -} - diff --git a/src/formats/brother.textpb b/src/formats/brother.textpb new file mode 100644 index 00000000..a5d1bf98 --- /dev/null +++ b/src/formats/brother.textpb @@ -0,0 +1,91 @@ +comment: 'Brother GCR family' + +image_reader { + filename: "brother.img" + type: IMG +} + +image_writer { + filename: "brother.img" + type: IMG +} + +encoder { + brother {} +} + +decoder { + brother {} +} + +option_group { + comment: "Format variant" + + option { + name: "120" + comment: '120kB 3.5" 39-track SS GCR' + + config { + layout { + tracks: 39 + sides: 1 + layoutdata { + sector_size: 256 + physical { + start_sector: 0 + count: 12 + skew: 5 + } + } + } + + encoder { + brother { + format: BROTHER120 + } + } + + drive { + head_bias: 0 + group_offset: 1 + } + + filesystem { + type: BROTHER120 + } + + tpi: 48 + } + } + + option { + name: "240" + comment: '240kB 3.5" 78-track SS GCR' + + config { + layout { + tracks: 78 + sides: 1 + layoutdata { + sector_size: 256 + physical { + start_sector: 0 + count: 12 + skew: 5 + } + } + } + + drive { + head_bias: 3 + } + + filesystem { + type: FATFS + } + + tpi: 96 + } + } +} + diff --git a/src/formats/brother120.textpb b/src/formats/brother120.textpb deleted file mode 100644 index 6bea37ca..00000000 --- a/src/formats/brother120.textpb +++ /dev/null @@ -1,46 +0,0 @@ -comment: 'Brother 120kB 3.5" 39-track SS GCR' - -image_reader { - filename: "brother120.img" - type: IMG -} - -image_writer { - filename: "brother120.img" - type: IMG -} - -layout { - tracks: 39 - sides: 1 - layoutdata { - sector_size: 256 - physical { - start_sector: 0 - count: 12 - skew: 5 - } - } -} - -encoder { - brother { - format: BROTHER120 - } -} - -decoder { - brother {} -} - -drive { - head_bias: 0 - group_offset: 1 -} - -tpi: 48 - -filesystem { - type: BROTHER120 -} - diff --git a/src/formats/brother240.textpb b/src/formats/brother240.textpb deleted file mode 100644 index 4f87cab6..00000000 --- a/src/formats/brother240.textpb +++ /dev/null @@ -1,41 +0,0 @@ -comment: 'Brother 240kB 3.5" 78-track SS GCR' - -image_reader { - filename: "brother240.img" - type: IMG -} - -image_writer { - filename: "brother240.img" - type: IMG -} - -layout { - tracks: 78 - sides: 1 - layoutdata { - sector_size: 256 - physical { - start_sector: 0 - count: 12 - skew: 5 - } - } -} - -encoder { - brother {} -} - -decoder { - brother {} -} - -drive { - head_bias: 3 -} - -filesystem { - type: FATFS -} - diff --git a/src/formats/build.mk b/src/formats/build.mk index 4554f84c..9fe82e4e 100644 --- a/src/formats/build.mk +++ b/src/formats/build.mk @@ -1,37 +1,16 @@ FORMATS = \ - _acornadfs8 \ - _acornadfs32 \ - _apple2 \ - _atari \ - _micropolis \ - _northstar \ - _mx \ 40track_drive \ - acornadfs160 \ - acornadfs320 \ - acornadfs640 \ - acornadfs800 \ - acornadfs1600 \ + acornadfs \ acorndfs \ aeslanier \ agat840 \ amiga \ - ampro400 \ - ampro800 \ + ampro \ apple2_drive \ - appleii140 \ - appleii640 \ - atarist360 \ - atarist370 \ - atarist400 \ - atarist410 \ - atarist720 \ - atarist740 \ - atarist800 \ - atarist820 \ + apple2 \ + atarist \ bk800 \ - brother120 \ - brother240 \ + brother \ commodore1541 \ commodore1581 \ cmd_fd2000 \ @@ -39,44 +18,22 @@ FORMATS = \ epsonpf10 \ f85 \ fb100 \ - hp9121 \ - hplif770 \ + hplif \ ibm \ - ibm1200 \ - ibm1232 \ - ibm1440 \ - ibm180 \ - ibm160 \ - ibm360 \ - ibm320 \ - ibm720 \ icl30 \ - mac400 \ - mac800 \ - micropolis143 \ - micropolis287 \ - micropolis315 \ - micropolis630 \ - mx110 \ - mx220_ds \ - mx220_ss \ - mx440 \ + mac \ + micropolis \ + mx \ n88basic \ - northstar175 \ - northstar350 \ - northstar87 \ + northstar \ psos800 \ rolandd20 \ rx50 \ shugart_drive \ smaky6 \ tids990 \ - tiki90 \ - tiki200 \ - tiki400 \ - tiki800 \ - victor9k_ds \ - victor9k_ss \ + tiki \ + victor9k \ zilogmcz \ $(OBJDIR)/src/formats/format_%.o: $(OBJDIR)/src/formats/format_%.cc diff --git a/src/formats/commodore1541.textpb b/src/formats/commodore1541.textpb index 31e01608..dcfca7bb 100644 --- a/src/formats/commodore1541.textpb +++ b/src/formats/commodore1541.textpb @@ -64,27 +64,30 @@ filesystem { type: CBMFS } -option { - name: "35" - comment: "35-track variant (default)" - message: "using 35-track variant" - - config { - layout { - tracks: 35 +option_group { + comment: "Format family" + + option { + name: "171" + comment: "35-track variant" + set_by_default: true + + config { + layout { + tracks: 35 + } + } + } + + option { + name: "192" + comment: "40-track variant" + + config { + layout { + tracks: 40 + } } } } - -option { - name: "40" - comment: "40-track variant" - message: "using 40-track variant" - - config { - layout { - tracks: 40 - } - } -} - \ No newline at end of file + diff --git a/src/formats/hp9121.textpb b/src/formats/hp9121.textpb deleted file mode 100644 index 0c45b2a7..00000000 --- a/src/formats/hp9121.textpb +++ /dev/null @@ -1,49 +0,0 @@ -comment: 'Hewlett-Packard 9121 264kB 3.5" SSDD' - -image_reader { - filename: "hp9121.img" - type: IMG -} - -layout { - tracks: 66 - sides: 1 - layoutdata { - sector_size: 256 - physical { - sector: 0 - sector: 4 - sector: 8 - sector: 12 - sector: 1 - sector: 5 - sector: 9 - sector: 13 - sector: 2 - sector: 6 - sector: 10 - sector: 14 - sector: 3 - sector: 7 - sector: 11 - sector: 15 - } - } -} - -encoder { - ibm { - trackdata { - emit_iam: false - target_rotational_period_ms: 200 - target_clock_period_us: 4 - gap0: 80 - gap2: 22 - gap3: 44 - } - } -} - -decoder { - ibm {} -} diff --git a/src/formats/hplif.textpb b/src/formats/hplif.textpb new file mode 100644 index 00000000..4d2e5677 --- /dev/null +++ b/src/formats/hplif.textpb @@ -0,0 +1,143 @@ +comment: 'Hewlett-Packard LIF family' + +drive { + high_density: false +} + +image_reader { + filename: "hplif.img" + type: IMG +} + +image_writer { + filename: "hplif.img" + type: IMG +} + +decoder { + ibm { + } +} + +filesystem { + type: LIF +} + +tpi: 96 + +option_group { + comment: "Format family" + + option { + name: "264" + comment: '264kB 3.5" 66-track SSDD; HP9121 format' + + config { + layout { + tracks: 66 + sides: 1 + layoutdata { + sector_size: 256 + physical { + sector: 0 + sector: 4 + sector: 8 + sector: 12 + sector: 1 + sector: 5 + sector: 9 + sector: 13 + sector: 2 + sector: 6 + sector: 10 + sector: 14 + sector: 3 + sector: 7 + sector: 11 + sector: 15 + } + } + } + + encoder { + ibm { + trackdata { + emit_iam: false + target_rotational_period_ms: 200 + target_clock_period_us: 4 + gap0: 80 + gap2: 22 + gap3: 44 + } + } + } + } + } + + option { + name: "616" + comment: '616kB 3.5" 77-track DSDD' + + config { + layout { + tracks: 77 + sides: 2 + layoutdata { + sector_size: 256 + physical { + start_sector: 1 + count: 16 + } + } + } + + encoder { + ibm { + trackdata { + target_rotational_period_ms: 200 + target_clock_period_us: 4 + emit_iam: false + gap0: 80 + gap2: 22 + gap3: 40 + } + } + } + } + } + + option { + name: "770" + comment: '770kB 3.5" 77-track DSDD' + + config { + layout { + tracks: 77 + sides: 2 + layoutdata { + sector_size: 1024 + physical { + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + } + } + } + + encoder { + ibm { + trackdata { + target_rotational_period_ms: 200 + target_clock_period_us: 4 + gap0: 80 + gap2: 22 + gap3: 80 + } + } + } + } + } +} + diff --git a/src/formats/hplif770.textpb b/src/formats/hplif770.textpb deleted file mode 100644 index b0a80567..00000000 --- a/src/formats/hplif770.textpb +++ /dev/null @@ -1,48 +0,0 @@ -comment: 'Hewlett-Packard LIF 770kB 3.5" DSDD' - -drive { - high_density: false - rotational_period_ms: 200 -} - -image_reader { - filename: "hplif770.img" - type: IMG -} - -image_writer { - filename: "hplif770.img" - type: IMG -} - -layout { - tracks: 77 - sides: 2 - layoutdata { - sector_size: 1024 - physical { - sector: 1 - sector: 2 - sector: 3 - sector: 4 - sector: 5 - } - } -} - -encoder { - ibm { - trackdata { - target_rotational_period_ms: 200 - target_clock_period_us: 4 - gap0: 80 - gap2: 22 - gap3: 80 - } - } -} - -decoder { - ibm { - } -} diff --git a/src/formats/ibm.textpb b/src/formats/ibm.textpb index 3f229a83..d9311fea 100644 --- a/src/formats/ibm.textpb +++ b/src/formats/ibm.textpb @@ -1,4 +1,9 @@ -comment: 'PC 3.5"/5.25" autodetect double sided format' +comment: 'Generic PC 3.5"/5.25" family' + +image_reader { + filename: "ibm.img" + type: IMG +} image_writer { filename: "ibm.img" @@ -9,9 +14,255 @@ decoder { ibm {} } -heads { - start: 0 - end: 1 +filesystem { + type: FATFS } +tpi: 96 + +option_group { + comment: "Format variant" + + option { + name: "auto" + comment: 'try to autodetect the format (unreliable)' + set_by_default: true + + config {} + } + + option { + name: "160" + comment: '160kB 5.25" 40-track 8-sector SSDD' + + config { + layout { + tracks: 40 + sides: 1 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 8 + } + } + } + + encoder { + ibm { + trackdata { + target_rotational_period_ms: 167 + target_clock_period_us: 3.333 + } + } + } + + tpi: 48 + } + } + + option { + name: "180" + comment: '180kB 5.25" 40-track 9-sector SSDD' + + config { + layout { + tracks: 40 + sides: 1 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 9 + } + } + } + + encoder { + ibm { + trackdata { + target_rotational_period_ms: 167 + target_clock_period_us: 3.333 + } + } + } + + tpi: 48 + } + } + + option { + name: "320" + comment: '320kB 5.25" 40-track 8-sector DSDD' + + config { + layout { + tracks: 40 + sides: 2 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 8 + } + } + } + + encoder { + ibm { + trackdata { + target_rotational_period_ms: 167 + target_clock_period_us: 3.333 + } + } + } + + tpi: 48 + } + } + + option { + name: "360" + comment: '360kB 5.25" 40-track 9-sector DSDD' + + config { + layout { + tracks: 40 + sides: 2 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 9 + } + } + } + + encoder { + ibm { + trackdata { + target_rotational_period_ms: 167 + target_clock_period_us: 3.333 + } + } + } + + tpi: 48 + } + } + + option { + name: "720" + comment: '720kB 5.25"/3.5" 80-track 9-sector DSDD' + + config { + layout { + tracks: 80 + sides: 2 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 9 + } + } + } + + encoder { + ibm { + trackdata { + # This also works on 166ms drives (producing a physical clock of + # 3.33us). + target_rotational_period_ms: 200 + target_clock_period_us: 4 + } + } + } + } + } + + option { + name: "1200" + comment: '1200kB 5.25" 80-track 15-sector DSHD' + + config { + layout { + tracks: 80 + sides: 2 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 15 + } + } + } + + encoder { + ibm { + trackdata { + target_rotational_period_ms: 167 + target_clock_period_us: 2 + } + } + } + } + } + + option { + name: "1232" + comment: '1232kB 5.25"/3.5" 77-track 8-sector DSHD' + + config { + layout { + tracks: 77 + sides: 2 + layoutdata { + sector_size: 1024 + physical { + start_sector: 1 + count: 8 + } + } + } + + encoder { + ibm { + trackdata { + target_rotational_period_ms: 167 + target_clock_period_us: 2 + } + } + } + } + } + + option { + name: "1440" + comment: '1440kB 3.5" 80-track 18-sector DSHD' + + config { + layout { + tracks: 80 + sides: 2 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 18 + } + } + } + + encoder { + ibm { + trackdata { + target_rotational_period_ms: 200 + target_clock_period_us: 2 + } + } + } + } + } +} diff --git a/src/formats/ibm1200.textpb b/src/formats/ibm1200.textpb deleted file mode 100644 index c624b1dd..00000000 --- a/src/formats/ibm1200.textpb +++ /dev/null @@ -1,46 +0,0 @@ -comment: 'PC 1200kB 5.25" 80-track 15-sector DSHD' - -image_reader { - filename: "ibm1200.img" - type: IMG -} - -image_writer { - filename: "ibm1200.img" - type: IMG -} - -layout { - tracks: 80 - sides: 2 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 15 - } - } -} - -encoder { - ibm { - trackdata { - target_rotational_period_ms: 167 - target_clock_period_us: 2 - } - } -} - -decoder { - ibm {} -} - -drive { - high_density: true -} - -filesystem { - type: FATFS -} - - diff --git a/src/formats/ibm1232.textpb b/src/formats/ibm1232.textpb deleted file mode 100644 index 8947ff97..00000000 --- a/src/formats/ibm1232.textpb +++ /dev/null @@ -1,46 +0,0 @@ -comment: 'Japanese PC 1232kB 5.25"/3.5" 77-track 8-sector DSHD' - -image_reader { - filename: "ibm1232.img" - type: IMG -} - -image_writer { - filename: "ibm1232.img" - type: IMG -} - -layout { - tracks: 77 - sides: 2 - layoutdata { - sector_size: 1024 - physical { - start_sector: 1 - count: 8 - } - } -} - -encoder { - ibm { - trackdata { - target_rotational_period_ms: 167 - target_clock_period_us: 2 - } - } -} - -decoder { - ibm {} -} - -drive { - high_density: true -} - -filesystem { - type: FATFS -} - - diff --git a/src/formats/ibm1440.textpb b/src/formats/ibm1440.textpb deleted file mode 100644 index e28490b9..00000000 --- a/src/formats/ibm1440.textpb +++ /dev/null @@ -1,41 +0,0 @@ -comment: 'PC 1440kB 3.5" 80-track 18-sector DSHD' - -image_reader { - filename: "ibm1440.img" - type: IMG -} - -image_writer { - filename: "ibm1440.img" - type: IMG -} - -layout { - tracks: 80 - sides: 2 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 18 - } - } -} - -encoder { - ibm { - trackdata { - target_rotational_period_ms: 200 - target_clock_period_us: 2 - } - } -} - -decoder { - ibm {} -} - -filesystem { - type: FATFS -} - diff --git a/src/formats/ibm160.textpb b/src/formats/ibm160.textpb deleted file mode 100644 index 498fb64f..00000000 --- a/src/formats/ibm160.textpb +++ /dev/null @@ -1,48 +0,0 @@ -comment: 'PC 160kB 5.25" 40-track 8-sector SSDD' - -image_reader { - filename: "ibm160.img" - type: IMG -} - -image_writer { - filename: "ibm160.img" - type: IMG -} - -layout { - tracks: 40 - sides: 1 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 8 - } - } -} - -encoder { - ibm { - trackdata { - target_rotational_period_ms: 167 - target_clock_period_us: 3.333 - } - } -} - -decoder { - ibm {} -} - -drive { - high_density: false -} - -tpi: 48 - -filesystem { - type: FATFS -} - - diff --git a/src/formats/ibm180.textpb b/src/formats/ibm180.textpb deleted file mode 100644 index 4bc63f82..00000000 --- a/src/formats/ibm180.textpb +++ /dev/null @@ -1,48 +0,0 @@ -comment: 'PC 180kB 5.25" 40-track 9-sector SSDD' - -image_reader { - filename: "ibm180.img" - type: IMG -} - -image_writer { - filename: "ibm180.img" - type: IMG -} - -layout { - tracks: 40 - sides: 1 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 9 - } - } -} - -encoder { - ibm { - trackdata { - target_rotational_period_ms: 167 - target_clock_period_us: 3.333 - } - } -} - -decoder { - ibm {} -} - -drive { - high_density: false -} - -tpi: 48 - -filesystem { - type: FATFS -} - - diff --git a/src/formats/ibm320.textpb b/src/formats/ibm320.textpb deleted file mode 100644 index 5ba7e3c8..00000000 --- a/src/formats/ibm320.textpb +++ /dev/null @@ -1,44 +0,0 @@ -comment: 'PC 320kB 5.25" 40-track 8-sector DSDD' - -image_reader { - filename: "ibm320.img" - type: IMG -} - -image_writer { - filename: "ibm320.img" - type: IMG -} - -layout { - tracks: 40 - sides: 2 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 8 - } - } -} - -encoder { - ibm { - trackdata { - target_rotational_period_ms: 167 - target_clock_period_us: 3.333 - } - } -} - -decoder { - ibm {} -} - -tpi: 48 - -filesystem { - type: FATFS -} - - diff --git a/src/formats/ibm360.textpb b/src/formats/ibm360.textpb deleted file mode 100644 index 780ed59f..00000000 --- a/src/formats/ibm360.textpb +++ /dev/null @@ -1,44 +0,0 @@ -comment: 'PC 360kB 5.25" 40-track 9-sector DSDD' - -image_reader { - filename: "ibm360.img" - type: IMG -} - -image_writer { - filename: "ibm360.img" - type: IMG -} - -layout { - tracks: 40 - sides: 2 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 9 - } - } -} - -encoder { - ibm { - trackdata { - target_rotational_period_ms: 167 - target_clock_period_us: 3.333 - } - } -} - -decoder { - ibm {} -} - -tpi: 48 - -filesystem { - type: FATFS -} - - diff --git a/src/formats/ibm720.textpb b/src/formats/ibm720.textpb deleted file mode 100644 index 9b4672b8..00000000 --- a/src/formats/ibm720.textpb +++ /dev/null @@ -1,47 +0,0 @@ -comment: 'PC 720kB 5.25"/3.5" 80-track 9-sector DSDD' - -image_reader { - filename: "ibm720.img" - type: IMG -} - -image_writer { - filename: "ibm720.img" - type: IMG -} - -layout { - tracks: 80 - sides: 2 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 9 - } - } -} - -encoder { - ibm { - trackdata { - # This also works on 166ms drives (producing a physical clock of - # 3.33us). - target_rotational_period_ms: 200 - target_clock_period_us: 4 - } - } -} - -decoder { - ibm {} -} - -drive { - high_density: false -} - -filesystem { - type: FATFS -} - diff --git a/src/formats/mac800.textpb b/src/formats/mac.textpb similarity index 53% rename from src/formats/mac800.textpb rename to src/formats/mac.textpb index 3f029f00..c4128bb1 100644 --- a/src/formats/mac800.textpb +++ b/src/formats/mac.textpb @@ -1,12 +1,12 @@ -comment: 'Macintosh 800kB 3.5" DSDD GCR' +comment: 'Macintosh GCR family' image_reader { - filename: "mac800.dsk" + filename: "mac.dsk" type: IMG } image_writer { - filename: "mac800.dsk" + filename: "mac.dsk" type: IMG } @@ -64,8 +64,47 @@ decoder { macintosh {} } -filesystem { - type: MACHFS +option_group { + comment: "Format variant" + + option { + name: "400" + comment: "400kB 80-track SSDD" + + config { + layout { + sides: 1 + } + } + } + + option { + name: "800" + comment: "800kB 80-track DSDD" + set_by_default: true + + config { + layout { + sides: 2 + } + + filesystem { + type: MACHFS + } + } + } } +option { + name: "metadata" + comment: "read/write 524 byte sectors" + + config { + layout { + layoutdata { + sector_size: 524 + } + } + } +} diff --git a/src/formats/mac400.textpb b/src/formats/mac400.textpb deleted file mode 100644 index 435c1ea8..00000000 --- a/src/formats/mac400.textpb +++ /dev/null @@ -1,66 +0,0 @@ -comment: 'Macintosh 400kB 3.5" SSDD GCR' - -image_reader { - filename: "mac400.dsk" - type: IMG -} - -image_writer { - filename: "mac400.dsk" - type: IMG -} - -layout { - tracks: 80 - sides: 1 - layoutdata { - sector_size: 512 - physical { - start_sector: 0 - skew: 6 - } - } - layoutdata { - track: 0 - up_to_track: 15 - physical { - count: 12 - } - } - layoutdata { - track: 16 - up_to_track: 31 - physical { - count: 11 - } - } - layoutdata { - track: 32 - up_to_track: 47 - physical { - count: 10 - } - } - layoutdata { - track: 48 - up_to_track: 63 - physical { - count: 9 - } - } - layoutdata { - track: 64 - up_to_track: 79 - physical { - count: 8 - } - } -} - -encoder { - macintosh {} -} - -decoder { - macintosh {} -} diff --git a/src/formats/micropolis.textpb b/src/formats/micropolis.textpb new file mode 100644 index 00000000..37b8a567 --- /dev/null +++ b/src/formats/micropolis.textpb @@ -0,0 +1,113 @@ +comment: ' Micropolis format family' + +drive { + hard_sector_count: 16 +} + +image_reader { + filename: "micropolis.img" + type: IMG +} + +image_writer { + filename: "micropolis.img" + type: IMG +} + +layout { + layoutdata { + sector_size: 256 + physical { + start_sector: 0 + count: 16 + } + } +} + +encoder { + micropolis {} +} + +decoder { + micropolis {} +} + +option { + name: "vgi" + comment: "Read/write VGI format images with 275 bytes per sector" + + config { + image_reader { + filename: "micropolis.vgi" + type: IMG + } + + image_writer { + filename: "micropolis.vgi" + type: IMG + } + + layout { + layoutdata { + sector_size: 275 + } + } + + decoder { + micropolis { + sector_output_size: 275 + } + } + } +} + +option_group { + option { + name: "143" + comment: '143kB 5.25" SSDD hard-sectored; Micropolis MetaFloppy Mod I' + + config { + layout { + tracks: 35 + sides: 1 + } + } + } + + option { + name: "287" + comment: '287kB 5.25" DSDD hard-sectored; Micropolis MetaFloppy Mod I' + + config { + layout { + tracks: 35 + sides: 2 + } + } + } + + option { + name: "315" + comment: '315kB 5.25" SSDD hard-sectored; Micropolis MetaFloppy Mod II' + + config { + layout { + tracks: 77 + sides: 1 + } + } + } + + option { + name: "630" + comment: '630kB 5.25" DSDD hard-sectored; Micropolis MetaFloppy Mod II' + + config { + layout { + tracks: 77 + sides: 2 + } + } + } +} + diff --git a/src/formats/micropolis143.textpb b/src/formats/micropolis143.textpb deleted file mode 100644 index 9a9ff523..00000000 --- a/src/formats/micropolis143.textpb +++ /dev/null @@ -1,8 +0,0 @@ -comment: 'Micropolis MetaFloppy Mod I 143kB 5.25" SSDD hard-sectored' - -include: '_micropolis' - -layout { - tracks: 35 - sides: 1 -} diff --git a/src/formats/micropolis287.textpb b/src/formats/micropolis287.textpb deleted file mode 100644 index 0c809d4a..00000000 --- a/src/formats/micropolis287.textpb +++ /dev/null @@ -1,8 +0,0 @@ -comment: 'Micropolis MetaFloppy Mod I 287kB 5.25" DSDD hard-sectored' - -include: '_micropolis' - -layout { - tracks: 35 - sides: 2 -} diff --git a/src/formats/micropolis315.textpb b/src/formats/micropolis315.textpb deleted file mode 100644 index acc8d305..00000000 --- a/src/formats/micropolis315.textpb +++ /dev/null @@ -1,8 +0,0 @@ -comment: 'Micropolis MetaFloppy Mod II 315kB 5.25" SSDD hard-sectored' - -include: '_micropolis' - -layout { - tracks: 77 - sides: 1 -} diff --git a/src/formats/micropolis630.textpb b/src/formats/micropolis630.textpb deleted file mode 100644 index d2bce476..00000000 --- a/src/formats/micropolis630.textpb +++ /dev/null @@ -1,8 +0,0 @@ -comment: 'Micropolis MetaFloppy Mod II 630kB 5.25" DSDD hard-sectored' - -include: '_micropolis' - -layout { - tracks: 77 - sides: 2 -} diff --git a/src/formats/mx.textpb b/src/formats/mx.textpb new file mode 100644 index 00000000..7da91c9b --- /dev/null +++ b/src/formats/mx.textpb @@ -0,0 +1,76 @@ +comment: 'DVK MX family' + +image_writer { + filename: "mx.img" + type: IMG +} + +layout { + layoutdata { + sector_size: 256 + physical { + start_sector: 0 + count: 11 + } + } +} + +decoder { + mx {} +} + +tpi: 48 + +option_group { + comment: "Format family" + + option { + name: "110" + comment: '110kB 5.25" 40-track SSSD' + + config { + layout { + tracks: 40 + sides: 1 + } + } + } + + option { + name: "220ds" + comment: '220kB 5.25" 40-track DSSD' + + config { + layout { + tracks: 40 + sides: 2 + } + } + } + + option { + name: "220ss" + comment: '220kB 5.25" 80-track SSSD' + + config { + layout { + tracks: 80 + sides: 1 + } + } + } + + option { + name: "440" + comment: '440kB 5.25" 80-track DSSD' + set_by_default: true + + config { + layout { + tracks: 80 + sides: 2 + } + } + } +} + diff --git a/src/formats/mx110.textpb b/src/formats/mx110.textpb deleted file mode 100644 index 1e20fe1a..00000000 --- a/src/formats/mx110.textpb +++ /dev/null @@ -1,11 +0,0 @@ -comment: 'DVK MX 110kB 5.25" (ro)' - -include: '_mx' - -layout { - tracks: 40 - sides: 1 -} - -tpi: 48 - diff --git a/src/formats/mx220_ds.textpb b/src/formats/mx220_ds.textpb deleted file mode 100644 index 5361200d..00000000 --- a/src/formats/mx220_ds.textpb +++ /dev/null @@ -1,11 +0,0 @@ -comment: 'DVK MX 220kB DS 40-track 5.25" (ro)' - -include: '_mx' - -layout { - tracks: 40 - sides: 2 -} - -tpi: 48 - diff --git a/src/formats/mx220_ss.textpb b/src/formats/mx220_ss.textpb deleted file mode 100644 index 55a2cbf5..00000000 --- a/src/formats/mx220_ss.textpb +++ /dev/null @@ -1,8 +0,0 @@ -comment: 'DVK MX 220kB SS 80-track 5.25" (ro)' - -include: '_mx' - -layout { - tracks: 80 - sides: 1 -} diff --git a/src/formats/mx440.textpb b/src/formats/mx440.textpb deleted file mode 100644 index c80a5ebc..00000000 --- a/src/formats/mx440.textpb +++ /dev/null @@ -1,8 +0,0 @@ -comment: 'DVK MX 440kB 5.25" (ro)' - -include: '_mx' - -layout { - tracks: 80 - sides: 2 -} diff --git a/src/formats/northstar.textpb b/src/formats/northstar.textpb new file mode 100644 index 00000000..012370cb --- /dev/null +++ b/src/formats/northstar.textpb @@ -0,0 +1,85 @@ +comment: 'Northstar family' + +image_reader { + filename: "northstar.nsi" + type: NSI +} + +image_writer { + filename: "northstar.nsi" + type: NSI +} + +layout { + layoutdata { + physical { + start_sector: 0 + count: 10 + } + } +} + +drive { + hard_sector_count: 10 + sync_with_index: true +} + +encoder { + northstar {} +} + +decoder { + northstar {} +} + +tpi: 48 + +option_group { + comment: "Format variant" + + option { + name: "87" + comment: '87.5kB 5.25" 35-track SSSD hard-sectored' + + config { + layout { + tracks: 40 + sides: 1 + layoutdata { + sector_size: 256 + } + } + } + } + + option { + name: "175" + comment: '175kB 5.25" 40-track SSDD hard-sectored' + + config { + layout { + tracks: 40 + sides: 1 + layoutdata { + sector_size: 512 + } + } + } + } + + option { + name: "350" + comment: '350kB 5.25" 40-track DSDD hard-sectored' + set_by_default: true + + config { + layout { + tracks: 40 + sides: 2 + layoutdata { + sector_size: 512 + } + } + } + } +} diff --git a/src/formats/northstar175.textpb b/src/formats/northstar175.textpb deleted file mode 100644 index f1042d84..00000000 --- a/src/formats/northstar175.textpb +++ /dev/null @@ -1,12 +0,0 @@ -comment: 'Northstar 175kB 5.25" 35-track SSDD hard-sectored' - -include: '_northstar' - -layout { - tracks: 40 - sides: 1 - layoutdata { - sector_size: 512 - } -} - diff --git a/src/formats/northstar350.textpb b/src/formats/northstar350.textpb deleted file mode 100644 index daeb8cf2..00000000 --- a/src/formats/northstar350.textpb +++ /dev/null @@ -1,13 +0,0 @@ -comment: 'Northstar 350kB 5.25" 35-track SSDD hard-sectored' - -include: '_northstar' - -layout { - tracks: 40 - sides: 2 - layoutdata { - sector_size: 512 - } -} - - diff --git a/src/formats/northstar87.textpb b/src/formats/northstar87.textpb deleted file mode 100644 index cb4c6939..00000000 --- a/src/formats/northstar87.textpb +++ /dev/null @@ -1,13 +0,0 @@ -comment: 'Northstar 87.5kB 5.25" 35-track SSSD hard-sectored' - -include: '_northstar' - -layout { - tracks: 40 - sides: 1 - layoutdata { - sector_size: 256 - } -} - - diff --git a/src/formats/tiki.textpb b/src/formats/tiki.textpb new file mode 100644 index 00000000..f158d73a --- /dev/null +++ b/src/formats/tiki.textpb @@ -0,0 +1,144 @@ +comment: 'Tiki 100 family' + +image_writer { + filename: "tiki.img" + type: IMG +} + +decoder { + ibm {} +} + +tpi: 48 + +option_group { + comment: "Format variant" + + option { + name: "90" + comment: '90kB 40-track 18-sector SSSD' + + config { + layout { + tracks: 40 + sides: 1 + layoutdata { + side: 0 + sector_size: 128 + physical { + start_sector: 1 + count: 18 + } + } + } + + filesystem { + type: CPMFS + cpmfs { + filesystem_start { + track: 3 + } + block_size: 1024 + dir_entries: 32 + } + } + } + } + + option { + name: "200" + comment: '200kB 40-track 10-sector SSSD' + + config { + layout { + tracks: 40 + sides: 1 + layoutdata { + side: 0 + sector_size: 512 + physical { + start_sector: 1 + count: 10 + } + } + } + + filesystem { + type: CPMFS + cpmfs { + filesystem_start { + track: 2 + } + block_size: 1024 + dir_entries: 64 + } + } + } + } + + option { + name: "400" + comment: '400kB 40-track 10-sector DSSD' + + config { + layout { + tracks: 40 + sides: 2 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 10 + } + } + } + + filesystem { + type: CPMFS + cpmfs { + filesystem_start { + side: 0 + track: 1 + } + block_size: 2048 + dir_entries: 128 + } + } + + } + } + + option { + name: "800" + comment: '800kB 80-track 10-sector DSSD' + + config { + layout { + tracks: 80 + sides: 2 + layoutdata { + sector_size: 512 + physical { + start_sector: 1 + count: 10 + } + } + } + + filesystem { + type: CPMFS + cpmfs { + filesystem_start { + side: 0 + track: 1 + } + block_size: 2048 + dir_entries: 128 + } + } + + tpi: 96 + } + } +} + diff --git a/src/formats/tiki200.textpb b/src/formats/tiki200.textpb deleted file mode 100644 index d672320f..00000000 --- a/src/formats/tiki200.textpb +++ /dev/null @@ -1,38 +0,0 @@ -comment: 'Tiki 100 200kB 40-track 10-sector SSSD (ro)' - -image_writer { - filename: "tiki200.img" - type: IMG -} - -layout { - tracks: 40 - sides: 1 - layoutdata { - side: 0 - sector_size: 512 - physical { - start_sector: 1 - count: 10 - } - } -} - -decoder { - ibm {} -} - -filesystem { - type: CPMFS - cpmfs { - filesystem_start { - track: 2 - } - block_size: 1024 - dir_entries: 64 - } -} - -tpi: 48 - - diff --git a/src/formats/tiki400.textpb b/src/formats/tiki400.textpb deleted file mode 100644 index 5de55b96..00000000 --- a/src/formats/tiki400.textpb +++ /dev/null @@ -1,38 +0,0 @@ -comment: 'Tiki 100 400kB 40-track 10-sector DSSD (ro)' - -image_writer { - filename: "tiki400.img" - type: IMG -} - -layout { - tracks: 40 - sides: 2 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 10 - } - } -} - -decoder { - ibm {} -} - -filesystem { - type: CPMFS - cpmfs { - filesystem_start { - side: 0 - track: 1 - } - block_size: 2048 - dir_entries: 128 - } -} - -tpi: 48 - - diff --git a/src/formats/tiki800.textpb b/src/formats/tiki800.textpb deleted file mode 100644 index 152d4844..00000000 --- a/src/formats/tiki800.textpb +++ /dev/null @@ -1,38 +0,0 @@ -comment: 'Tiki 100 800kB 80-track 10-sector DSSD (ro)' - -image_writer { - filename: "tiki900.img" - type: IMG -} - -layout { - tracks: 80 - sides: 2 - layoutdata { - sector_size: 512 - physical { - start_sector: 1 - count: 10 - } - } -} - -decoder { - ibm {} -} - -filesystem { - type: CPMFS - cpmfs { - filesystem_start { - side: 0 - track: 1 - } - block_size: 2048 - dir_entries: 128 - } -} - -tpi: 96 - - diff --git a/src/formats/tiki90.textpb b/src/formats/tiki90.textpb deleted file mode 100644 index b4d7d7ff..00000000 --- a/src/formats/tiki90.textpb +++ /dev/null @@ -1,38 +0,0 @@ -comment: 'Tiki 100 90kB 40-track 18-sector SSSD (ro)' - -image_writer { - filename: "tiki90.img" - type: IMG -} - -layout { - tracks: 40 - sides: 1 - layoutdata { - side: 0 - sector_size: 128 - physical { - start_sector: 1 - count: 18 - } - } -} - -decoder { - ibm {} -} - -filesystem { - type: CPMFS - cpmfs { - filesystem_start { - track: 3 - } - block_size: 1024 - dir_entries: 32 - } -} - -tpi: 48 - - diff --git a/src/formats/victor9k_ds.textpb b/src/formats/victor9k.textpb similarity index 90% rename from src/formats/victor9k_ds.textpb rename to src/formats/victor9k.textpb index f49cf4d3..99894be0 100644 --- a/src/formats/victor9k_ds.textpb +++ b/src/formats/victor9k.textpb @@ -1,12 +1,12 @@ -comment: 'Victor 9000 / Sirius One 1224kB DSHD GCR variable sector)' +comment: 'Victor 9000 / Sirius One family' image_reader { - filename: "victor9k_ds.img" + filename: "victor9k.img" type: IMG } image_writer { - filename: "victor9k_ds.img" + filename: "victor9k.img" type: IMG } @@ -261,3 +261,30 @@ encoder { decoder { victor9k {} } + +option_group { + comment: "Format family" + + option { + name: "612" + comment: '612kB 80-track DSHD GCR' + + config { + layout { + sides: 1 + } + } + } + + option { + name: "1224" + comment: '1224kB 80-track DSHD GCR' + + config { + layout { + sides: 2 + } + } + } +} + diff --git a/src/formats/victor9k_ss.textpb b/src/formats/victor9k_ss.textpb deleted file mode 100644 index 9f4e32cb..00000000 --- a/src/formats/victor9k_ss.textpb +++ /dev/null @@ -1,188 +0,0 @@ -comment: 'Victor 9000 / Sirius One 612kB SSHD GCR variable sector)' - -image_reader { - filename: "victor9k.img" - type: IMG -} - -image_writer { - filename: "victor9k.img" - type: IMG -} - -layout { - tracks: 80 - sides: 1 - layoutdata { - sector_size: 512 - physical { - start_sector: 0 - } - } - layoutdata { - track: 0 - up_to_track: 3 - physical { - count: 19 - } - } - layoutdata { - track: 4 - up_to_track: 15 - physical { - count: 18 - } - filesystem { - start_sector: 0 - count: 18 - skew: 4 - } - } - layoutdata { - track: 16 - up_to_track: 26 - physical { - count: 17 - } - filesystem { - start_sector: 0 - count: 17 - skew: 4 - } - } - layoutdata { - track: 27 - up_to_track: 37 - physical { - count: 16 - } - filesystem { - start_sector: 0 - count: 16 - skew: 4 - } - } - layoutdata { - track: 38 - up_to_track: 47 - physical { - count: 15 - } - filesystem { - start_sector: 0 - count: 15 - skew: 4 - } - } - layoutdata { - track: 48 - up_to_track: 59 - physical { - count: 14 - } - filesystem { - start_sector: 0 - count: 14 - skew: 4 - } - } - layoutdata { - track: 60 - up_to_track: 70 - physical { - count: 13 - } - filesystem { - start_sector: 0 - count: 13 - skew: 4 - } - } - layoutdata { - track: 71 - up_to_track: 79 - physical { - count: 12 - } - filesystem { - start_sector: 0 - count: 12 - skew: 4 - } - } -} - -encoder { - victor9k { - trackdata { - clock_period_us: 2.1367 # 468kHz - post_index_gap_us: 500.0 - pre_header_sync_bits: 150 - post_header_gap_bits: 60 - pre_data_sync_bits: 40 - post_data_gap_bits: 300 - } - trackdata { - head: 0 - min_track: 0 - max_track: 3 - rotational_period_ms: 237.9 - } - trackdata { - head: 0 - min_track: 4 - max_track: 15 - rotational_period_ms: 224.5 - } - trackdata { - head: 0 - min_track: 16 - max_track: 26 - rotational_period_ms: 212.2 - } - trackdata { - head: 0 - min_track: 27 - max_track: 37 - rotational_period_ms: 199.9 - } - trackdata { - head: 0 - min_track: 38 - max_track: 47 - rotational_period_ms: 187.6 - } - trackdata { - head: 0 - min_track: 48 - max_track: 59 - rotational_period_ms: 175.3 - } - trackdata { - head: 0 - min_track: 60 - max_track: 70 - rotational_period_ms: 163.0 - } - trackdata { - head: 0 - min_track: 71 - max_track: 79 - rotational_period_ms: 149.6 - } - } -} - -decoder { - victor9k {} -} - -filesystem { - cpmfs { - filesystem_start { - track: 5 - } - block_size: 2048 - dir_entries: 128 - } -} diff --git a/src/gui/idlepanel.cc b/src/gui/idlepanel.cc index cb300e80..f53ada58 100644 --- a/src/gui/idlepanel.cc +++ b/src/gui/idlepanel.cc @@ -577,89 +577,85 @@ private: _formatNames[formatChoice->GetSelection()]; FlagGroup::parseConfigFile(formatName, formats); - std::set exclusivityGroups; - for (auto& option : config.option()) + for (auto& group : config.option_group()) { - if (option.has_exclusivity_group()) - exclusivityGroups.insert(option.exclusivity_group()); - } - - if (config.option().empty()) sizer->Add(new wxStaticText(formatOptionsContainer, wxID_ANY, - "(no options for this format)")); - else - { - /* Add grouped radiobuttons for anything in an exclusivity - * group. */ + group.comment() + ":")); - for (auto& group : exclusivityGroups) + bool first = true; + bool valueSet = false; + wxRadioButton* defaultButton = nullptr; + for (auto& option : group.option()) { - bool first = true; - for (auto& option : config.option()) - { - if (option.exclusivity_group() != group) - continue; - - auto* rb = new wxRadioButton(formatOptionsContainer, - wxID_ANY, - option.comment()); - auto key = - std::make_pair(formatName, option.name()); - sizer->Add(rb); - - rb->Bind(wxEVT_RADIOBUTTON, - [=](wxCommandEvent& e) - { - for (auto& option : config.option()) - { - if (option.exclusivity_group() == group) - _formatOptions.erase(std::make_pair( - formatName, option.name())); - } - - _formatOptions.insert(key); - - OnControlsChanged(e); - }); - - if (_formatOptions.find(key) != - _formatOptions.end()) - rb->SetValue(true); - - if (first) - rb->SetExtraStyle(wxRB_GROUP); - first = false; - } - } - - /* Anything that's _not_ in a group gets a checkbox. */ - - for (auto& option : config.option()) - { - if (option.has_exclusivity_group()) - continue; - - auto* choice = new wxCheckBox( - formatOptionsContainer, wxID_ANY, option.comment()); + auto* rb = new wxRadioButton(formatOptionsContainer, + wxID_ANY, + option.comment(), + wxDefaultPosition, + wxDefaultSize, + first ? wxRB_GROUP : 0); auto key = std::make_pair(formatName, option.name()); - sizer->Add(choice); + sizer->Add(rb); - if (_formatOptions.find(key) != _formatOptions.end()) - choice->SetValue(true); - - choice->Bind(wxEVT_CHECKBOX, + rb->Bind(wxEVT_RADIOBUTTON, [=](wxCommandEvent& e) { - if (choice->GetValue()) - _formatOptions.insert(key); - else - _formatOptions.erase(key); + for (auto& option : group.option()) + { + _formatOptions.erase(std::make_pair( + formatName, option.name())); + } + + _formatOptions.insert(key); OnControlsChanged(e); }); + + if (_formatOptions.find(key) != _formatOptions.end()) + { + rb->SetValue(true); + valueSet = true; + } + + if (option.set_by_default() || !defaultButton) + defaultButton = rb; + + first = false; } + + if (!valueSet && defaultButton) + defaultButton->SetValue(true); } + + /* Anything that's _not_ in a group gets a checkbox. */ + + for (auto& option : config.option()) + { + auto* choice = new wxCheckBox( + formatOptionsContainer, wxID_ANY, option.comment()); + auto key = std::make_pair(formatName, option.name()); + sizer->Add(choice); + + choice->SetValue( + (_formatOptions.find(key) != _formatOptions.end()) || + option.set_by_default()); + + choice->Bind(wxEVT_CHECKBOX, + [=](wxCommandEvent& e) + { + if (choice->GetValue()) + _formatOptions.insert(key); + else + _formatOptions.erase(key); + + OnControlsChanged(e); + }); + } + + if (config.option().empty() && config.option_group().empty()) + sizer->Add(new wxStaticText(formatOptionsContainer, + wxID_ANY, + "(no options for this format)")); } formatOptionsContainer->SetSizerAndFit(sizer);