mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	Merge pull request #667 from davidgiven/options
Overhaul the options system.
This commit is contained in:
		
							
								
								
									
										54
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								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 $@) | ||||
|   | ||||
| @@ -19,13 +19,15 @@ Reading discs | ||||
| Just do: | ||||
|  | ||||
| ``` | ||||
| fluxengine read ampro400 | ||||
| fluxengine read ampro <format> | ||||
| ``` | ||||
|  | ||||
| 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 `<format>` 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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -33,29 +33,29 @@ FluxEngine can also write Atari ST scheme disks. | ||||
|  | ||||
| The syntax is: | ||||
|  | ||||
|     fluxengine write <format> -i input.st | ||||
|     fluxengine write atarist -i input.st <format> | ||||
|  | ||||
| Available formats | ||||
| ----------------- | ||||
|  | ||||
| `<format>` 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. | ||||
|   | ||||
| @@ -38,10 +38,10 @@ Reading disks | ||||
| Just do: | ||||
|  | ||||
| ``` | ||||
| fluxengine read `<format>` | ||||
| fluxengine read brother `<format>` | ||||
| ``` | ||||
|  | ||||
| ... where `<format>` can be `brother120` or `brother240`. You should end up | ||||
| ... where `<format>` 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 `<format>` -i brother.img | ||||
| ``` | ||||
|  | ||||
| ...where `<format>` can be `brother120` or `brother240`. | ||||
| ...where `<format>` 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 | ||||
|  | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -38,15 +38,16 @@ Reading disks | ||||
|  | ||||
| Just do: | ||||
|  | ||||
|     fluxengine read `<format>` | ||||
|     fluxengine read ibm `<format>` | ||||
|  | ||||
| ...and you'll end up with a `<format>.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 <format> -i input.img <options> | ||||
|     fluxengine write ibm <format> -i input.img <options> | ||||
|  | ||||
| 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 | ||||
|   | ||||
| @@ -37,20 +37,19 @@ Reading discs | ||||
| Just do: | ||||
|  | ||||
| ``` | ||||
| fluxengine read <format> -o mac.dsk | ||||
| fluxengine read mac <format> -o mac.dsk | ||||
| ``` | ||||
|  | ||||
| ...where `<format>` can be `mac400` or `mac800`. | ||||
| ...where `<format>` 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 <format> -i mac.dsk | ||||
| fluxengine write mac <format> -i mac.dsk | ||||
| ``` | ||||
|  | ||||
| ...where `<format>` can be `mac400` or `mac800`. | ||||
| ...where `<format>` 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 | ||||
| ----------------- | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -9,7 +9,7 @@ clone of the PDP-11. The MX board was an early floppy drive controller board | ||||
| for it. | ||||
|  | ||||
| <div style="text-align: center"> | ||||
| <a href="http://www.leningrad.su/museum/show_big.php?n=1006"><img src="dvk3m.jpg" style="max-width: 60%" alt="A Durango F85, held precariously"></a> | ||||
| <a href="http://www.leningrad.su/museum/show_big.php?n=1006"><img src="dvk3m.jpg" style="max-width: 60%" alt="A DVK computer"></a> | ||||
| </div> | ||||
|  | ||||
| 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 | ||||
|   | ||||
| @@ -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 <format> | ||||
| fluxengine read northstar <format> | ||||
| ``` | ||||
|  | ||||
| ...where `<format>` 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 <format> -i image_to_write.nsi | ||||
| fluxengine write northstar <format> -i image_to_write.nsi | ||||
| ``` | ||||
|  | ||||
| ...where `<format>` 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 | ||||
| ----------------- | ||||
|   | ||||
| @@ -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 <format> | ||||
| fluxengine read victor9k <format> | ||||
|  | ||||
| ``` | ||||
|  | ||||
| ...where `<format>` 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 `<format>` 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 <format> -i victor9k.img | ||||
| ``` | ||||
|  | ||||
| **Big warning!** This uses the same triangular disk image that reading uses. | ||||
|  | ||||
| `<format>` is as above. | ||||
|  | ||||
| Useful references | ||||
| ----------------- | ||||
|   | ||||
| @@ -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 \ | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										788
									
								
								lib/flags.cc
									
									
									
									
									
								
							
							
						
						
									
										788
									
								
								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 <google/protobuf/text_format.h> | ||||
| #include <regex> | ||||
| #include <fstream> | ||||
|  | ||||
| static FlagGroup* currentFlagGroup; | ||||
| static std::vector<Flag*> all_flags; | ||||
| static std::map<const std::string, Flag*> 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<FlagGroup*> 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<std::string> FlagGroup::parseFlagsWithFilenames(int argc, | ||||
|     const char* argv[], | ||||
|     std::function<bool(const std::string&)> 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<void(FlagGroup*)> 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<std::string> 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<bool(const std::string&)> 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<std::string, std::string>& configFiles) | ||||
| { | ||||
|     parseFlags(argc, | ||||
|         argv, | ||||
|         [&](const auto& filename) | ||||
|         { | ||||
|             parseConfigFile(filename, configFiles); | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
|  | ||||
| ConfigProto FlagGroup::parseSingleConfigFile(const std::string& filename, | ||||
|     const std::map<std::string, std::string>& 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<std::string, std::string>& 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<std::string>& 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 << " <default: \"" << flag->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 <google/protobuf/text_format.h> | ||||
| #include <regex> | ||||
| #include <fstream> | ||||
|  | ||||
| static FlagGroup* currentFlagGroup; | ||||
| static std::vector<Flag*> all_flags; | ||||
| static std::map<const std::string, Flag*> 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<FlagGroup*> 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<std::string> FlagGroup::parseFlagsWithFilenames(int argc, | ||||
|     const char* argv[], | ||||
|     std::function<bool(const std::string&)> 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<void(FlagGroup*)> 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<std::string> options; | ||||
|     std::vector<std::pair<std::string, std::string>> overrides; | ||||
|     std::vector<std::string> 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<bool(const std::string&)> 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<std::string, std::string>& configFiles) | ||||
| { | ||||
|     parseFlags(argc, | ||||
|         argv, | ||||
|         [&](const auto& filename) | ||||
|         { | ||||
|             parseConfigFile(filename, configFiles); | ||||
|             return true; | ||||
|         }); | ||||
| } | ||||
|  | ||||
| ConfigProto FlagGroup::parseSingleConfigFile(const std::string& filename, | ||||
|     const std::map<std::string, std::string>& 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<std::string, std::string>& 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<std::string>& 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 << " <default: \"" << flag->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); | ||||
| } | ||||
|   | ||||
							
								
								
									
										656
									
								
								lib/flags.h
									
									
									
									
									
								
							
							
						
						
									
										656
									
								
								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<FlagGroup*> groups); | ||||
|  | ||||
| public: | ||||
|     void parseFlags( | ||||
|         int argc, | ||||
|         const char* argv[], | ||||
|         std::function<bool(const std::string&)> callback = | ||||
|             [](const auto&) | ||||
|         { | ||||
|             return false; | ||||
|         }); | ||||
|     std::vector<std::string> parseFlagsWithFilenames( | ||||
|         int argc, | ||||
|         const char* argv[], | ||||
|         std::function<bool(const std::string&)> callback = | ||||
|             [](const auto&) | ||||
|         { | ||||
|             return false; | ||||
|         }); | ||||
|     void parseFlagsWithConfigFiles(int argc, | ||||
|         const char* argv[], | ||||
|         const std::map<std::string, std::string>& configFiles); | ||||
|  | ||||
|     /* Load one config file (or internal config file), without expanding | ||||
|      * includes. */ | ||||
|  | ||||
|     static ConfigProto parseSingleConfigFile(const std::string& filename, | ||||
|         const std::map<std::string, std::string>& configFiles); | ||||
|  | ||||
|     /* Load a top-level config file (or internal config file), expanding | ||||
|      * includes. */ | ||||
|  | ||||
|     static void parseConfigFile(const std::string& filename, | ||||
|         const std::map<std::string, std::string>& 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<FlagGroup*> _groups; | ||||
|     std::vector<Flag*> _flags; | ||||
| }; | ||||
|  | ||||
| class Flag | ||||
| { | ||||
| public: | ||||
|     Flag(const std::vector<std::string>& names, const std::string helptext); | ||||
|     virtual ~Flag(){}; | ||||
|  | ||||
|     void checkInitialised() const | ||||
|     { | ||||
|         _group.checkInitialised(); | ||||
|     } | ||||
|  | ||||
|     const std::string& name() const | ||||
|     { | ||||
|         return _names[0]; | ||||
|     } | ||||
|     const std::vector<std::string> 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<std::string> _names; | ||||
|     const std::string _helptext; | ||||
| }; | ||||
|  | ||||
| class ActionFlag : Flag | ||||
| { | ||||
| public: | ||||
|     ActionFlag(const std::vector<std::string>& names, | ||||
|         const std::string helptext, | ||||
|         std::function<void(void)> 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<void(void)> _callback; | ||||
| }; | ||||
|  | ||||
| class SettableFlag : public Flag | ||||
| { | ||||
| public: | ||||
|     SettableFlag( | ||||
|         const std::vector<std::string>& 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 <typename T> | ||||
| class ValueFlag : public Flag | ||||
| { | ||||
| public: | ||||
|     ValueFlag( | ||||
|         const std::vector<std::string>& names, | ||||
|         const std::string helptext, | ||||
|         const T defaultValue, | ||||
|         std::function<void(const T&)> 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<void(const T&)> _callback; | ||||
| }; | ||||
|  | ||||
| class StringFlag : public ValueFlag<std::string> | ||||
| { | ||||
| public: | ||||
|     StringFlag( | ||||
|         const std::vector<std::string>& names, | ||||
|         const std::string helptext, | ||||
|         const std::string defaultValue = "", | ||||
|         std::function<void(const std::string&)> 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<int> | ||||
| { | ||||
| public: | ||||
|     IntFlag( | ||||
|         const std::vector<std::string>& names, | ||||
|         const std::string helptext, | ||||
|         int defaultValue = 0, | ||||
|         std::function<void(const int&)> 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<std::string>& names, | ||||
|         const std::string helptext, | ||||
|         int defaultValue = 0, | ||||
|         std::function<void(const int&)> callback = | ||||
|             [](const int&) | ||||
|         { | ||||
|         }): | ||||
|         IntFlag(names, helptext, defaultValue, callback) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     const std::string defaultValueAsString() const; | ||||
| }; | ||||
|  | ||||
| class DoubleFlag : public ValueFlag<double> | ||||
| { | ||||
| public: | ||||
|     DoubleFlag( | ||||
|         const std::vector<std::string>& names, | ||||
|         const std::string helptext, | ||||
|         double defaultValue = 1.0, | ||||
|         std::function<void(const double&)> 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<bool> | ||||
| { | ||||
| public: | ||||
|     BoolFlag( | ||||
|         const std::vector<std::string>& names, | ||||
|         const std::string helptext, | ||||
|         bool defaultValue = false, | ||||
|         std::function<void(const bool&)> 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<FlagGroup*> groups); | ||||
|  | ||||
| public: | ||||
|     void parseFlags( | ||||
|         int argc, | ||||
|         const char* argv[], | ||||
|         std::function<bool(const std::string&)> callback = | ||||
|             [](const auto&) | ||||
|         { | ||||
|             return false; | ||||
|         }); | ||||
|     std::vector<std::string> parseFlagsWithFilenames( | ||||
|         int argc, | ||||
|         const char* argv[], | ||||
|         std::function<bool(const std::string&)> callback = | ||||
|             [](const auto&) | ||||
|         { | ||||
|             return false; | ||||
|         }); | ||||
|     void parseFlagsWithConfigFiles(int argc, | ||||
|         const char* argv[], | ||||
|         const std::map<std::string, std::string>& configFiles); | ||||
|  | ||||
|     /* Load one config file (or internal config file), without expanding | ||||
|      * includes. */ | ||||
|  | ||||
|     static ConfigProto parseSingleConfigFile(const std::string& filename, | ||||
|         const std::map<std::string, std::string>& configFiles); | ||||
|  | ||||
|     /* Load a top-level config file (or internal config file), expanding | ||||
|      * includes. */ | ||||
|  | ||||
|     static void parseConfigFile(const std::string& filename, | ||||
|         const std::map<std::string, std::string>& 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<FlagGroup*> _groups; | ||||
|     std::vector<Flag*> _flags; | ||||
| }; | ||||
|  | ||||
| class Flag | ||||
| { | ||||
| public: | ||||
|     Flag(const std::vector<std::string>& names, const std::string helptext); | ||||
|     virtual ~Flag(){}; | ||||
|  | ||||
|     void checkInitialised() const | ||||
|     { | ||||
|         _group.checkInitialised(); | ||||
|     } | ||||
|  | ||||
|     const std::string& name() const | ||||
|     { | ||||
|         return _names[0]; | ||||
|     } | ||||
|     const std::vector<std::string> 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<std::string> _names; | ||||
|     const std::string _helptext; | ||||
| }; | ||||
|  | ||||
| class ActionFlag : Flag | ||||
| { | ||||
| public: | ||||
|     ActionFlag(const std::vector<std::string>& names, | ||||
|         const std::string helptext, | ||||
|         std::function<void(void)> 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<void(void)> _callback; | ||||
| }; | ||||
|  | ||||
| class SettableFlag : public Flag | ||||
| { | ||||
| public: | ||||
|     SettableFlag( | ||||
|         const std::vector<std::string>& 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 <typename T> | ||||
| class ValueFlag : public Flag | ||||
| { | ||||
| public: | ||||
|     ValueFlag( | ||||
|         const std::vector<std::string>& names, | ||||
|         const std::string helptext, | ||||
|         const T defaultValue, | ||||
|         std::function<void(const T&)> 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<void(const T&)> _callback; | ||||
| }; | ||||
|  | ||||
| class StringFlag : public ValueFlag<std::string> | ||||
| { | ||||
| public: | ||||
|     StringFlag( | ||||
|         const std::vector<std::string>& names, | ||||
|         const std::string helptext, | ||||
|         const std::string defaultValue = "", | ||||
|         std::function<void(const std::string&)> 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<int> | ||||
| { | ||||
| public: | ||||
|     IntFlag( | ||||
|         const std::vector<std::string>& names, | ||||
|         const std::string helptext, | ||||
|         int defaultValue = 0, | ||||
|         std::function<void(const int&)> 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<std::string>& names, | ||||
|         const std::string helptext, | ||||
|         int defaultValue = 0, | ||||
|         std::function<void(const int&)> callback = | ||||
|             [](const int&) | ||||
|         { | ||||
|         }): | ||||
|         IntFlag(names, helptext, defaultValue, callback) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     const std::string defaultValueAsString() const; | ||||
| }; | ||||
|  | ||||
| class DoubleFlag : public ValueFlag<double> | ||||
| { | ||||
| public: | ||||
|     DoubleFlag( | ||||
|         const std::vector<std::string>& names, | ||||
|         const std::string helptext, | ||||
|         double defaultValue = 1.0, | ||||
|         std::function<void(const double&)> 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<bool> | ||||
| { | ||||
| public: | ||||
|     BoolFlag( | ||||
|         const std::vector<std::string>& names, | ||||
|         const std::string helptext, | ||||
|         bool defaultValue = false, | ||||
|         std::function<void(const bool&)> callback = | ||||
|             [](const bool&) | ||||
|         { | ||||
|         }): | ||||
|         ValueFlag(names, helptext, defaultValue, callback) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     const std::string defaultValueAsString() const | ||||
|     { | ||||
|         return _defaultValue ? "true" : "false"; | ||||
|     } | ||||
|     void set(const std::string& value); | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										200
									
								
								lib/vfs/lif.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								lib/vfs/lif.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,200 @@ | ||||
| #include "lib/globals.h" | ||||
| #include "lib/vfs/vfs.h" | ||||
| #include "lib/config.pb.h" | ||||
| #include "lib/utils.h" | ||||
| #include <fmt/format.h> | ||||
|  | ||||
| /* 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<SectorInterface> 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<std::string, std::string> getMetadata() override | ||||
|     { | ||||
|         mount(); | ||||
|  | ||||
|         std::map<std::string, std::string> 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<Dirent> getDirent(const Path& path) override | ||||
|     { | ||||
|         mount(); | ||||
|         if (path.size() != 1) | ||||
|             throw BadPathException(); | ||||
|  | ||||
|         return findFile(path.front()); | ||||
|     } | ||||
|  | ||||
|     std::vector<std::shared_ptr<Dirent>> list(const Path& path) override | ||||
|     { | ||||
|         mount(); | ||||
|         if (!path.empty()) | ||||
|             throw FileNotFoundException(); | ||||
|  | ||||
|         std::vector<std::shared_ptr<Dirent>> 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<LifDirent>(_config, direntBytes); | ||||
|                 _usedBlocks += dirent->length / _config.block_size(); | ||||
|                 _dirents.push_back(std::move(dirent)); | ||||
|             } | ||||
|         } | ||||
|         _totalBlocks = std::max(tracks * heads * sectors, _usedBlocks); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<LifDirent> 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<std::shared_ptr<LifDirent>> _dirents; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<Filesystem> Filesystem::createLifFilesystem( | ||||
|     const FilesystemProto& config, std::shared_ptr<SectorInterface> sectors) | ||||
| { | ||||
|     return std::make_unique<LifFilesystem>(config.lif(), sectors); | ||||
| } | ||||
| @@ -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); | ||||
|   | ||||
| @@ -212,6 +212,9 @@ std::unique_ptr<Filesystem> 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<Filesystem>(); | ||||
|   | ||||
| @@ -254,6 +254,8 @@ public: | ||||
|         const FilesystemProto& config, std::shared_ptr<SectorInterface> image); | ||||
|     static std::unique_ptr<Filesystem> createPhileFilesystem( | ||||
|         const FilesystemProto& config, std::shared_ptr<SectorInterface> image); | ||||
|     static std::unique_ptr<Filesystem> createLifFilesystem( | ||||
|         const FilesystemProto& config, std::shared_ptr<SectorInterface> image); | ||||
|  | ||||
|     static std::unique_ptr<Filesystem> createFilesystem( | ||||
|         const FilesystemProto& config, std::shared_ptr<SectorInterface> image); | ||||
|   | ||||
| @@ -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" ]; | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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 {} | ||||
| } | ||||
|  | ||||
| @@ -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 {} | ||||
| } | ||||
|  | ||||
							
								
								
									
										121
									
								
								src/formats/acornadfs.textpb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								src/formats/acornadfs.textpb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 			 | ||||
| @@ -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 | ||||
| @@ -1,10 +0,0 @@ | ||||
| comment: 'Acorn ADFS F-format 1600kB 3.5" 80-track DS' | ||||
| include: '_acornadfs32' | ||||
|  | ||||
| layout { | ||||
| 	layoutdata { | ||||
| 		physical { | ||||
| 			count: 10 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
| @@ -1,10 +0,0 @@ | ||||
| comment: 'Acorn ADFS D and E-format 800kB 3.5" 80-track DS' | ||||
| include: '_acornadfs32' | ||||
|  | ||||
| layout { | ||||
| 	layoutdata { | ||||
| 		physical { | ||||
| 			count: 5 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										58
									
								
								src/formats/ampro.textpb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/formats/ampro.textpb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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 {} | ||||
| } | ||||
| @@ -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 {} | ||||
| } | ||||
							
								
								
									
										254
									
								
								src/formats/apple2.textpb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								src/formats/apple2.textpb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
							
								
								
									
										180
									
								
								src/formats/atarist.textpb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								src/formats/atarist.textpb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| 					} | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
							
								
								
									
										91
									
								
								src/formats/brother.textpb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/formats/brother.textpb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -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 | ||||
| } | ||||
|  | ||||
| @@ -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 | ||||
| } | ||||
|  | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 	 | ||||
| 		 | ||||
|   | ||||
| @@ -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 {} | ||||
| } | ||||
							
								
								
									
										143
									
								
								src/formats/hplif.textpb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/formats/hplif.textpb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -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 { | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -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 | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -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 | ||||
| } | ||||
|  | ||||
| @@ -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 | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -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 | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -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 | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -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 | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -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 | ||||
| } | ||||
|  | ||||
| @@ -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 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @@ -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 {} | ||||
| } | ||||
							
								
								
									
										113
									
								
								src/formats/micropolis.textpb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								src/formats/micropolis.textpb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -1,8 +0,0 @@ | ||||
| comment: 'Micropolis MetaFloppy Mod I 143kB 5.25" SSDD hard-sectored' | ||||
|  | ||||
| include: '_micropolis' | ||||
|  | ||||
| layout { | ||||
| 	tracks: 35 | ||||
| 	sides: 1 | ||||
| } | ||||
| @@ -1,8 +0,0 @@ | ||||
| comment: 'Micropolis MetaFloppy Mod I 287kB 5.25" DSDD hard-sectored' | ||||
|  | ||||
| include: '_micropolis' | ||||
|  | ||||
| layout { | ||||
| 	tracks: 35 | ||||
| 	sides: 2 | ||||
| } | ||||
| @@ -1,8 +0,0 @@ | ||||
| comment: 'Micropolis MetaFloppy Mod II 315kB 5.25" SSDD hard-sectored' | ||||
|  | ||||
| include: '_micropolis' | ||||
|  | ||||
| layout { | ||||
| 	tracks: 77 | ||||
| 	sides: 1 | ||||
| } | ||||
| @@ -1,8 +0,0 @@ | ||||
| comment: 'Micropolis MetaFloppy Mod II 630kB 5.25" DSDD hard-sectored' | ||||
|  | ||||
| include: '_micropolis' | ||||
|  | ||||
| layout { | ||||
| 	tracks: 77 | ||||
| 	sides: 2 | ||||
| } | ||||
							
								
								
									
										76
									
								
								src/formats/mx.textpb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								src/formats/mx.textpb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -1,11 +0,0 @@ | ||||
| comment: 'DVK MX 110kB 5.25" (ro)' | ||||
|  | ||||
| include: '_mx' | ||||
|  | ||||
| layout { | ||||
| 	tracks: 40 | ||||
| 	sides: 1 | ||||
| } | ||||
|  | ||||
| tpi: 48 | ||||
|  | ||||
| @@ -1,11 +0,0 @@ | ||||
| comment: 'DVK MX 220kB DS 40-track 5.25" (ro)' | ||||
|  | ||||
| include: '_mx' | ||||
|  | ||||
| layout { | ||||
| 	tracks: 40 | ||||
| 	sides: 2 | ||||
| } | ||||
|  | ||||
| tpi: 48 | ||||
|  | ||||
| @@ -1,8 +0,0 @@ | ||||
| comment: 'DVK MX 220kB SS 80-track 5.25" (ro)' | ||||
|  | ||||
| include: '_mx' | ||||
|  | ||||
| layout { | ||||
| 	tracks: 80 | ||||
| 	sides: 1 | ||||
| } | ||||
| @@ -1,8 +0,0 @@ | ||||
| comment: 'DVK MX 440kB 5.25" (ro)' | ||||
|  | ||||
| include: '_mx' | ||||
|  | ||||
| layout { | ||||
| 	tracks: 80 | ||||
| 	sides: 2 | ||||
| } | ||||
							
								
								
									
										85
									
								
								src/formats/northstar.textpb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/formats/northstar.textpb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -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 | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -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 | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
							
								
								
									
										144
									
								
								src/formats/tiki.textpb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								src/formats/tiki.textpb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
|  | ||||
| @@ -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 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @@ -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 | ||||
|     } | ||||
| } | ||||
| @@ -577,89 +577,85 @@ private: | ||||
|                     _formatNames[formatChoice->GetSelection()]; | ||||
|                 FlagGroup::parseConfigFile(formatName, formats); | ||||
|  | ||||
|                 std::set<std::string> 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); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user