fluxctl is a modular toolkit for inspecting and converting floppy disk flux captures. It supports decoding flux streams, reconstructing sectors, quality control, visualization, extraction, and exporting to standard image formats.
The easiest source-checkout install is:
git clone https://github.com/GeoKM/fluxctl.git
cd fluxctl
python3 scripts/install_fluxctl.py --yes --greaseweazle --clone-greaseweazle --clone-hxcfe --build-hxcfe
.venv/bin/fluxctl doctorOn Windows PowerShell, use the Python launcher and Windows script paths:
New Windows setup? Start with the Windows prerequisites and installation guide for required downloads, optional build tools, architecture selection, and verification steps.
git clone https://github.com/GeoKM/fluxctl.git
cd fluxctl
py -3 scripts\install_fluxctl.py --yes --greaseweazle --clone-greaseweazle --clone-hxcfe --build-hxcfe --build-native
.venv\Scripts\fluxctl.exe doctor
.venv\Scripts\fluxctl.exe --help
.venv\Scripts\fluxctl-studio.exeOn Windows, Fluxctl itself needs Git and Python. Optional helper builds need extra native build tools:
- Greaseweazle: install Microsoft C++ Build Tools 14.0 or newer with the "Desktop development with C++" workload.
- HxCFE: install a GNU Make/GCC environment such as MSYS2/MinGW64, or use a
prebuilt
hxcfe.exeand pass--hxcfe C:\path\to\hxcfe.exe. - Rust native acceleration: install Rust from
https://rustup.rsand Microsoft C++ Build Tools 14.0 or newer with the "Desktop development with C++" workload so the MSVC linkerlink.exeis available.--build-nativedetects the virtual environment's Python architecture and selects the matching Rust target. This matters on Windows ARM64, where an x64 Python may run under emulation and cannot load an ARM64 DLL.
This creates .venv, installs fluxctl and Fluxctl Studio, offers optional
Greaseweazle support, clones/builds optional HxCFE support, and prints the
installed command paths. Run .venv/bin/fluxctl --help to explore available
targets.
For a minimal manual install instead:
python3 -m venv .venv
.venv/bin/python -m pip install --upgrade pip
.venv/bin/python -m pip install -e .For an interactive source-checkout install that can also offer GUI and optional Greaseweazle/HxCFE setup checks, run:
python3 scripts/install_fluxctl.py- info: inspect SCP headers and inferred geometry.
- doctor: check the local installation and optional helper integrations.
- probe: detect encoding, layout, and filesystem (where detectable) for SCP, WOZ, Apple II
.po/.do/.nib, IMD, TRS-80.dsk/.dmk, and flat images. - compare: hash + byte diff two images; SCP inputs are decoded first.
- roundtrip: convert through an intermediate format and verify decoded-sector hashes after each leg.
- qc: generate quality control reports (JSON or text).
- visualize: render ASCII or SVG disk maps.
- extract: detect filesystems and extract files or raw sectors.
- convert: export to raw, IMD, ADF, D64, D71, D81, G64, Apple II PO/DO, and deterministic synthetic SCP images.
- sectors/dump/patch: per-track listing, hex dump, and simple patching helpers.
- studio: optional desktop GUI for guided and advanced workflows.
- Encodings: MFM, FM, Commodore GCR, and Apple II 16-sector 6-and-2 GCR via plugin registry.
- Physical layouts include IBM PC FAT geometries, IBM XDF OS/2 media, Commodore/Amiga formats, CP/M machine formats, Tandy TRS-80 formats, RT-11 RX01/RX02 RT-11 media, IBM DisplayWriter mixed-sector media, and Apple II 35-track 140K media in WOZ, NIB, PO, DO, DSK/IMG, or decoded SCP form.
- Tandy TRS-80 Model II CP/M 625K media is supported in two mixed-density
variants: track 0 FM 26x128 with tracks 1-76 MFM 8x1024
(
tandy_trs80_model2_cpm_625k) or MFM 16x512 (tandy_trs80_model2_cpm_16x512_625k). SCP and IMD retain this physical distinction; a flat 625,920-byte IMG cannot distinguish the two layouts by itself. - Filesystems detected: FAT12, CBM DOS, Apple ProDOS, Apple DOS 3.3, CP/M (C64 CP/M 2.2, C128 CP/M 3.0, Osborne/Kaypro/Tandy variants), TRSDOS 1.3, LDOS/TRSDOS 6, NEWDOS/80, Amiga OFS/FFS, RT-11 (normal RX02 volumes and RX01/IBM 3740 Interchange labels), Displaywriter probe, read-only Seiko 8300 EBCDIC catalog/dataset readers, and read-only Wang OIS installation-package catalogs; raw sector dumps always supported.
- Filesystem listing, extraction, export, and copy-only mutation support varies by format. See the filesystem capability matrix for current limitations.
fluxctl doctor
fluxctl info disk.scp
fluxctl probe disk.scp
fluxctl qc disk.scp --layout ibm_mfm_720k
fluxctl extract disk.img --listUse explicit JSON options when the result will be consumed by another program or retained as a regression artifact:
fluxctl doctor --json
fluxctl qc disk.scp --layout ibm_mfm_720k --json-out qc.json
fluxctl compare before.img after.img --json-out diff.json
fluxctl roundtrip disk.scp --layout amiga_mfm_880k --to adf \\
--json-out roundtrip.json
fluxctl recover disk.scp --layout ibm_mfm_720k --policy best-effort \\
--out repaired.img --manifest recovery.json# Compare two images (SCP decoded on the fly)
fluxctl compare a.scp b.img --json-out diff.json
fluxctl compare before.img after.img
# Verify conversion losslessness through decoded sector hashes. Round-trip
# checks compare reconstructed sector bytes, physical sector metadata, and
# readable filesystem file hashes, not raw flux timing bytes.
fluxctl roundtrip amiga.scp --layout amiga_mfm_880k --to adf --json-out roundtrip.json
fluxctl roundtrip disk.adf --to raw --back-to adf --work-dir /tmp/fluxctl-roundtrip
# The report separates data, logical geometry, and preservation equivalence.
# Recover competing SCP revolutions into a new image. The source is never
# modified; the decision manifest is written beside the repaired image.
fluxctl recover damaged.scp --layout ibm_mfm_720k --policy strict-crc \
--out damaged-recovered.img
# Generate a calibrated SCP via Greaseweazle's selected format encoder. This
# is useful for hardware writing and sector-level round-trip checking, but it
# cannot recreate the original capture's analogue timing or protection data.
fluxctl synthesize-scp disk.img --format ibm.720 --out disk-synthesized.scp
fluxctl compare disk.img disk-synthesized.scp \
--layout-a ibm_mfm_720k --layout-b ibm_mfm_720k
# Destructive hardware write: Greaseweazle verifies the write, Fluxctl then
# captures a new raw SCP and compares decoded sectors. Source images are never
# modified and the JSON manifest records both commands and results.
fluxctl write disk.img --format ibm.720 --layout ibm_mfm_720k \
--readback-out disk-readback.scp --confirm-write
# Quality reports and maps
fluxctl qc disk.scp --json-out qc.json
fluxctl visualize disk.scp --format ascii --out map.txt
# Export / convert
fluxctl convert disk.scp --to raw --out disk.img
fluxctl convert disk.scp --to raw --out disk.img --layout ibm_mfm_720k
fluxctl convert disk.img --to imd --out disk.imd --layout ibm_mfm_720k
fluxctl convert trs80.dsk --to imd --out trs80.imd
fluxctl convert trs80.dmk --to imd --out trs80.imd
fluxctl convert c64.scp --to d64 --out disk.d64
fluxctl convert c64.scp --to g64 --out disk.g64 --layout commodore_gcr_1541_170k
fluxctl convert c128.scp --to d71 --out disk.d71 --layout commodore_gcr_1571_341k
fluxctl convert 1581.scp --to d81 --out disk.d81 --layout commodore_mfm_1581_800k
fluxctl convert disk.d81 --to raw --out disk.img
fluxctl convert disk.img --to raw --out copy.img
fluxctl convert disk.adf --to scp --out disk.scp
fluxctl convert disk.img --layout ibm_mfm_720k --to scp --out disk.scp
fluxctl roundtrip disk.adf --to scp --back-to adf
fluxctl roundtrip disk.img --layout ibm_mfm_720k --to scp --back-to raw
fluxctl extract disk.img --list
fluxctl extract disk.img --path FILE.TXT --out output.bin
fluxctl extract disk.scp --layout ibm_mfm_720k --path README.TXT --out readme.binFor Commodore layouts, QC JSON/text reports and Studio map details also include
conservative CBM DOS command-channel codes inferred from decoded sectors. An
unrecovered expected sector is reported as 22 READ ERROR (data block not present), and recovered data with a failed checksum is reported as 23 READ ERROR (checksum error in data block). These codes describe the closest CBM
DOS read diagnosis; the original drive may have reported 20/21/24/27
depending on whether the failure occurred in the header, sync, or byte
decoder. Write-protect, write-verify, long-block, and disk-ID errors cannot be
proven from a read-only image and are never guessed.
# Per-track inspection
fluxctl sectors disk.scp --track 0 --head 0 --encoding mfm
fluxctl dump disk.scp --layout ibm_mfm_720k --track 0 --side 0 --sector 1
# Patch one full sector and export a raw image
fluxctl patch disk.scp --layout ibm_mfm_720k --write-sector 0:0:1:DEADBEEF... --out patched.imgConversion targets are checked against the resolved layout, encoding, geometry,
and container semantics before an exporter runs. The CLI and Fluxctl Studio use
the same compatibility planner. A route is reported as sector-lossless,
logically-equivalent, or lossy-but-useful; incompatible targets are rejected
before an output file is created. A sector-equivalent conversion may still lose
physical flux timing, track encoding, weak bits, or copy-protection details.
Commands that create user-visible files refuse to overwrite an existing path
by default. compare, qc, visualize, convert, roundtrip, extract, and
patch accept --force when replacement is intentional. Fluxctl writes each
file to a temporary file in the destination directory and publishes it only
after the complete output has been flushed, so an interrupted write does not
leave a partially written destination.
Fluxctl also checks all known outputs, including provenance and patch-log
sidecars, before starting a command. An input image can never be used as its own
output path, even with --force.
Use fluxctl doctor when a conversion, decode, or optional helper path behaves
unexpectedly. It reports:
- Python and fluxctl versions.
- Loaded layouts, decoders, exporters, and filesystem readers.
- Whether optional Rust native decoder acceleration is enabled, disabled, or not built yet.
- Whether the optional Greaseweazle Python package can be imported.
- Whether
hxcfeis available onPATH, at a supplied--hxcfepath, or in a sibling../HxCFloppyEmulatorcheckout built by the installer.
Examples:
fluxctl doctor
fluxctl doctor --json
fluxctl doctor --hxcfe ~/src/HxCFloppyEmulator/HxCFloppyEmulator_cmdline/build/hxcfeWarnings are informational for optional features. A missing native library only
means fluxctl will use the pure-Python decoder path. A failed hxcfe check only
matters when you explicitly want HxC-assisted hints.
Fluxctl Studio is an optional desktop interface for the same core operations as the CLI. It is designed around two workflows:
- Simple Mode: open an image, run doctor/probe/QC, render a disk map, list filesystem files, inspect file/sector hex, export files, replace supported files into a new image copy, create common blank disk images, and convert common output formats with fewer choices.
- Advanced Mode: expose layout, encoding, track/head/sector, compare, sector listing, hex dump, conversion, and provenance inspection controls.
Install the GUI dependency and launch it:
.venv/bin/python -m pip install -e ".[gui]"
.venv/bin/fluxctl-studioStudio calls the shared fluxctl application and domain operations directly; it does not drive the CLI through subprocess command paths. This keeps the GUI and CLI on the same conversion, filesystem, output-safety, provenance, and recovery rules while allowing each frontend to present results in its own workflow.
Long-running Studio operations are shown in the Jobs & Logs tab with an elapsed time indicator and a cancellation control. Cancellation is cooperative: an operation that is already inside a non-interruptible decoder may finish in the background, but its result is discarded. Starting a newer operation or opening another image likewise prevents an older result from replacing current-screen data. Window layout, mode, map view, format selections, and common browsing or Greaseweazle settings are saved with the operating system's application settings.
For SCP inputs, convert auto-detects the likely layout when --layout is not
provided. Pass --layout when you want to force a specific interpretation or
when a damaged/ambiguous capture cannot be identified confidently.
Native convert --to scp uses the same compatibility planner as Studio. It
supports standard IBM-style FM/MFM, native Amiga MFM, Commodore GCR, and Apple
II 16-sector 6-and-2 GCR layouts. Dedicated containers such as ADF, D64, D71,
and D81 resolve their layouts automatically; ambiguous flat IMG files require
--layout. The result is deterministic logical flux suitable for conversion,
round-trip testing, emulation, and supported hardware-writing workflows. It is
not a preservation substitute for an original capture: analogue timing,
write-splice placement, weak bits, and protection-specific patterns are not
recreated. Specialised hard-sector, XDF, RX02, MMFM, and other unsupported
mixed-track formats are rejected instead of being encoded with an incorrect
generic track. The supported Tandy Model II mixed-density layouts are explicit
exceptions and must be selected when converting a flat IMG.
File replacement is intentionally conservative. Studio always writes a new
image copy instead of modifying the original image. Current replacement support
includes FAT12 files in flat .img images, root-level CBM DOS files in .d64
and .d71, nested CBM DOS 1581 files in .d81, and same-size Amiga OFS/FFS
files in .adf. FAT12 replacements may grow the selected file by allocating
free clusters; CBM and Amiga operations apply their format-specific allocation
and metadata rules. Replacement for other filesystems or image containers that
would require unsupported sector rewrites is rejected until a dedicated writer
exists.
Studio also supports FAT12 .img file manipulation into new image copies:
delete a file or empty directory, import a file, recursively import a directory
tree, and create an empty directory. FAT12 import and directory creation
currently require 8.3-compatible ASCII names and reject overwriting existing
entries.
CBM DOS .d64 and .d71 images support root-level file import, replacement,
and scratch/delete in a new image copy. Import uses ASCII names up to 16
characters; .PRG, .SEQ, and .USR suffixes select the CBM file type and
unknown suffixes default to PRG. REL mutation remains unavailable because it
requires side-sector allocation. CBM DOS 1581 .d81 additionally supports
nested file operations, recursive directory import, directory creation, and
empty-directory deletion.
For CBM DOS sector hex and dump controls, Studio accepts Commodore logical track
numbers: the 1541/1571 BAM is entered as track 18, head 0, sector 0.
- D64: reconstructed 256-byte logical sectors written to a flat image. This is convenient for filesystem access but loses per-track GCR details and any copy-protection data.
- G64: preserves the decoded GCR nibble stream for each track in a half-track container. This format retains gaps and sync marks for better fidelity in emulators, but currently derives half-tracks from full-track captures only (no separate half-track decoding yet).
- Execute
.venv/bin/python -m pytestafter activating the venv to cover CLI helpers, decoding, exporters, and filesystems. - The repository also includes
tests/fixtureswith annotated samples so you can run targeted commands against known media. - For full CLI validation across SCP fixtures (with longer GCR timeouts), run
scripts/fixture_cli_smoke.py.
Fluxctl is licensed under the GNU General Public License v3.0 or later
(GPL-3.0-or-later). See LICENSE for the full license text.
On macOS, optional helper builds need Apple's Command Line Tools for clang,
make, and system headers:
xcode-select --install
Full Xcode is usually not required.
Fluxctl can fall back to Greaseweazle’s Amiga and IBM FM/MFM codecs for higher-fidelity PLL decoding. This is optional; when missing, fluxctl uses its own PLL/parser.
Steps:
- Get Fluxctl's Greaseweazle support dependencies:
.venv/bin/pip install -e ".[greaseweazle]" - Install the actual Greaseweazle Python package into the same venv. The
source-checkout installer can do this automatically if a sibling checkout
exists, or can clone it with
--clone-greaseweazle; otherwise clone Greaseweazle alongside fluxctl and install it editable:git clone https://github.com/keirf/Greaseweazle.git ../greaseweazle .venv/bin/pip install -e ../greaseweazle
No configuration is required; fluxctl will auto-detect the import at runtime when present.
With the gw command available, fluxctl synthesize-scp creates a new SCP
using Greaseweazle's selected disk format encoder. This is calibrated logical
flux, not a preservation-grade recreation of a capture: it cannot retain weak
bits, original timing variation, non-standard gaps, or copy protection.
This Greaseweazle-backed command remains useful when its current disk-definition
catalog has a specialised encoder. The self-contained fluxctl convert --to scp path instead uses Fluxctl's native exporter and participates directly in
the standard compatibility planner, provenance, atomic-output, and roundtrip
workflows.
fluxctl write is deliberately destructive and requires --confirm-write.
It leaves Greaseweazle's own verification enabled, then performs a separate raw
SCP read-back for the requested number of revolutions and compares decoded
sector data using the supplied Fluxctl --layout. It retains the read-back SCP
and a JSON manifest containing hashes, commands, Greaseweazle output, and the
comparison result. Use a specific Greaseweazle --format; format-free writes
are refused.
Fluxctl Studio exposes the same flow in the Hardware panel. Load and probe
an image, select a Greaseweazle format, then use Synthesize SCP... or
Write Disk.... Writing requires typing WRITE and choosing a read-back SCP
destination.
HxCFE can provide layout hints and ADF conversions for Amiga and other formats.
Steps:
- Clone and build
hxcfe(the CLI from HxCFloppyEmulator). The source-checkout installer can clone it with--clone-hxcfeand attempt the Makefile build with--build-hxcfe; manually:git clone https://github.com/jfdelnero/HxCFloppyEmulator.git ../HxCFloppyEmulator make -C ../HxCFloppyEmulator/build HxCFloppyEmulator_cmdline - Point fluxctl at the binary when running commands that accept
--hxcfe, e.g.:fluxctl qc disk.scp --layout amiga_mfm_880k --hxcfe ~/src/HxCFloppyEmulator/HxCFloppyEmulator_cmdline/build/hxcfe fluxctl probe disk.scp --hxcfe /path/to/hxcfe
HxCFE is optional; when omitted, fluxctl uses its own detectors.
Fluxctl can load an optional Rust native library for hot flux-to-bitcell decoder loops. The Python implementation remains the fallback when the library is not built.
Build the native library from the repository root:
cargo build --manifest-path native/fluxctl_native/Cargo.toml --release
On Windows, the safer source-install route is
py -3 scripts\install_fluxctl.py --build-native; it builds for the Python
process architecture. For a manual build, use the target printed by
fluxctl doctor, for example:
rustup target add x86_64-pc-windows-msvc
cargo build --manifest-path native\fluxctl_native\Cargo.toml --release --target x86_64-pc-windows-msvcIf cargo is missing, install Rust first. The standard cross-platform path is:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Then open a new shell, or run source "$HOME/.cargo/env", and retry the
cargo build command. On Debian/Ubuntu packaged Rust can also be installed with
sudo apt install cargo rustc, but rustup usually provides a newer toolchain.
On Windows, install Rust from https://rustup.rs and Microsoft C++ Build Tools
14.0 or newer with the "Desktop development with C++" workload. If cargo
reports link.exe is missing, run the build from Developer PowerShell for
Visual Studio or a Native Tools command prompt so the MSVC linker is on PATH.
Use the x64 Native Tools prompt for x86_64-pc-windows-msvc, or the ARM64
Native Tools prompt for aarch64-pc-windows-msvc. If the linker reports
LNK4272, the prompt and Rust target do not match. For a native ARM64 build:
"C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" arm64
Fluxctl auto-detects the resulting library under native/fluxctl_native/target.
Set FLUXCTL_NATIVE_PATH=/path/to/libfluxctl_native.dylib (or .so/.dll) to
override the lookup path. Set FLUXCTL_DISABLE_NATIVE=1 to force the pure
Python fallback.
If a built library still shows as unavailable, fluxctl doctor reports the
native load error. On Windows, errors such as "not a valid Win32 application" or
LNK4272 usually mean the Python architecture, Rust target, and Visual Studio
Native Tools prompt do not all match. Do not use platform.machine() to choose
the DLL architecture on Windows ARM64: it may report the host (ARM64) for an
emulated x64 Python. Fluxctl uses Python's win-amd64/win-arm64 platform tag
and reads the DLL's PE header directly.
See AGENTS.md for coding standards, workflows, and review expectations. See docs/packaging.md for wheel, source distribution, and standalone GUI/CLI packaging notes. See docs/windows-prerequisites.md for the Windows software checklist and installation walkthrough.
Commands that create output files write provenance sidecars by default:
convert --out disk.imgwritesdisk.img.provenance.json.qc --json-out qc.jsonor--text-out qc.txtwrites a sidecar for the first report path.visualize --out map.txtwritesmap.txt.provenance.json.extract --out file.binwritesfile.bin.provenance.json.compare --json-out diff.jsonwritesdiff.json.provenance.json.patch --out patched.imgwritespatched.img.provenance.jsonand a separate patch log.
Use --prov-out custom.provenance.json on commands that support it to choose
the sidecar path. Terminal-only commands such as info, probe, sectors,
dump, and extract --list print results but do not create sidecars unless a
file output option is used. Provenance records capture tool version, inputs,
outputs, hashes, parameters, timestamps, and decoder/exporter identifiers so
artefacts can be verified later.