Repository navigation
v1.2.0 — WinImage .imz, CD-R capacity meter, every flavor of Linux, real crash fix
FloppyBootCD v1.2.0 — WinImage .imz, CD-R capacity meter, every flavor of Linux, real crash fix
A genuine feature release. v1.2.0 lands two long-asked-for capabilities — direct WinImage .imz support and a live CD-R capacity meter — plus six new Linux artifact formats, on top of finally fixing the GUI crash that survived two earlier patch attempts.
✨ New features
WinImage .imz compressed floppy images
WinImage's .imz format (ZIP-wrapped raw floppy image) is the most common shipping format for vintage floppy archives — every "boot disk image" pack on archive.org and bbsarchives uses it. Drop them directly into a project; the inner image is extracted to the build's staging tree, so MEMDISK still sees a normal raw floppy. No manual unzipping, no separate tool, no temp file shepherding.
- File dialog and drag-drop now accept
.imzalongside.img/.ima/.vfd/.flp - Project validation catches every
.imzfailure mode up-front (non-ZIP container, no floppy member inside, encrypted, unsupported compression method, 0-byte inner, corrupt CRC) — no more mid-build stack traces - Image labels show the uncompressed inner size, not the archive size, because that's what actually lands on the burned disc
- Project files store only the original
.imzpath; extraction happens entirely in the throwaway staging dir and never touches user paths - Extension allowlist is now centralized in
floppybootcd.core.image_prep(FLOPPY_EXTS/COMPRESSED_EXTS/ALL_ACCEPTED_EXTS) — single source of truth for the file dialog, drag-drop, and inner-member detection
CD-R capacity meter
A new permanent status-bar label, Disc usage: X.X MiB / 700 MiB, refreshed on every project change. Turns amber near the limit and red when over capacity, so you find out the project doesn't fit before you click Build instead of mid-xorriso.
validate_projectblocks builds whose payload exceeds 700 MiB usable capacity with an actionable message ("remove images or burn to DVD media") so xorriso doesn't fail mysteriously on a write that wouldn't have fit- Capacity math uses the uncompressed inner-image size for
.imzcontainers and the VESA background bitmap when in vesa-menu mode (since that gets staged onto the disc), giving an accurate pre-build budget - All capacity reporting uses MiB (binary 700 × 1024 × 1024) to match the on-disc math — described as "700 MiB CD-R" since binary 700 MiB (734 MB decimal) fits comfortably in the 80-min CD-R's real 737 MB capacity
- Inner-size lookups are LRU-cached by
(path, mtime, size), so a 50-image project doesn't re-read every ZIP central directory on every keystroke
Linux: AppImage, .deb, .rpm — and tarball
Linux now ships in four formats per arch (six new artifacts total), so picking an install matches how you install software on your machine — not "extract this tarball and figure out the rest."
| Format | Best for | Install |
|---|---|---|
| AppImage | Any distro, "double-click to run" | chmod +x *.AppImage && ./*.AppImage |
| .deb | Debian, Ubuntu, Raspberry Pi OS, Pop!_OS, Mint | sudo apt install ./*.deb |
| .rpm | Fedora, RHEL, Rocky, Alma, openSUSE | sudo dnf install ./*.rpm |
| .tar.gz | Read-only filesystems, locked-down servers, advanced users | tar xzf *.tar.gz && cd floppybootcd && ./floppybootcd |
Each format ships for both x86_64 and arm64 (Raspberry Pi 3/4/5/Zero 2 W on a 64-bit OS included).
The .deb and .rpm packages declare their Qt runtime dependencies (libxkbcommon, libxcb-*, libegl, fontconfig, libdbus, …) so apt/dnf surface a clear error on a stripped-down install — instead of the silent Qt "could not find platform plugin xcb" failure earlier versions could hit.
How it's built: PyInstaller bundle staged under /usr/lib/floppybootcd/ with a /usr/bin wrapper, an XDG .desktop entry, and the floppy-disk PNG icon. fpm wraps that into .deb and .rpm with per-distro dependency name lists (apt's libxcb-icccm4 vs. dnf's xcb-util-wm). appimagetool wraps an AppDir variant whose AppRun resolves the binary via $APPDIR so the image is relocatable. CI runs appimagetool with --appimage-extract-and-run so the GitHub runner doesn't need FUSE2.
🐛 Fixes
GUI crash on build/burn (real fix, supersedes v1.1.2 and v1.1.3)
Clicking Build ISO or Burn crashed the app within seconds — EXC_BAD_ACCESS on macOS, silent disappearance on Linux/Windows. v1.1.2 was a partial fix that just moved the crash from "first progress update" to "first dialog after finish." v1.2.0 is the real fix.
Root cause: the build/burn worker emits Qt signals (progress, log, finished) from its QThread to update GUI widgets on the main thread. In Qt/C++, the auto-selected connection type would queue these across threads. In PySide6, plain Python methods used as slots are wrapped in a "proxy" QObject whose thread affinity ends up wrong, so the queued events were being delivered to the worker thread's event loop — and QProgressBar.setValue() called repaint() off the GUI thread → "Recursive repaint detected" → segfault. v1.1.2 tried Qt.QueuedConnection explicitly, but the proxy was still wrong-threaded; the events queued onto the worker.
Real fix: decorate every cross-thread slot with @Slot(...). That bypasses the proxy entirely and makes the method a native Qt slot bound to its self QObject's thread. Reference: Martin Fitzpatrick's PySide6 @Slot explainer — "the only situation where you need @Slot decorators is when working with threads." The QThread::wait: Thread tried to wait on itself warning in earlier crashes was the smoking gun that the slot ran on the worker thread (where self._build_thread.wait() is the thread waiting on itself).
.imz polish (during stabilization)
- Reject empty-inner (0-byte)
.imzfiles at validation time with a clear message instead of letting them fail mid-build - Strip duplicated filename prefixes from
.imzvalidation error messages (was "Image 1 (foo.imz): foo.imz: …") - Catch mid-extraction
BadZipFile/OSError/RuntimeError/NotImplementedErrorand re-raise as the same user-facingValueError, so no.imzvariant can produce a mid-build stack trace - The capacity label no longer recomputes on every keystroke; refreshes only when inputs that actually affect it change (image add/remove, menu-style switch, project load)
- A previously-missing file edited to a valid path no longer stays styled red until the project is reloaded
- Missing files render as "missing" in the image label; only files that exist but fail to probe show "? (invalid .imz)"
Pages workflow reliability
The site auto-update pipeline that publishes https://pacnpal.github.io/floppybootcd/ had three compounding bugs all surfaced by recent releases:
- Unicode escape blowup. Release names with em-dashes encoded as
—byjson.dumps, thenre.subparsed them as regex template escapes →re.error: bad escape \u. Fixed withensure_ascii=Falseand lambda-replacement (which bypasses template parsing). - Race with binary upload. Pages workflow fired immediately on tag push, but release.yml was still building binaries — so it injected the just-created release with
assets: [], and the client renderer flashed through "(not in this release)" rows before flipping to "Couldn't fetch release info." Fixed by chaining: Pages now runs onworkflow_run: ["Release Binaries", types: completed]and the build job walks the releases list to pick the most recent one withassets | length > 0. - Renderer overwrote good content with errors. The client
render()would generate placeholder rows and then callrenderError()when nothing matched, even if a preloaded snapshot or earlier fetch had already populated a valid table. Rewritten to return success/failure and to only show errors when no data source produced a useful render.
Tests
- New coverage:
.imzwith empty inner image, capacity label states (empty / raw /.imzuncompressed accounting / over-capacity styling / within-capacity), VESA background counted vs. text-menu background ignored, drop-event Windows path normalization, encrypted.imzhex-patching walks every ZIP signature instead of stopping at the first match
📦 Downloads
| Platform | File |
|---|---|
| macOS (Apple Silicon) | floppybootcd-1.2.0-macos-arm64.zip |
| macOS (Intel) | floppybootcd-1.2.0-macos-x86_64.zip |
| Windows (x86_64) | floppybootcd-1.2.0-windows-x86_64.zip |
| Windows (ARM64) | floppybootcd-1.2.0-windows-arm64.zip |
| Linux AppImage (x86_64) | floppybootcd-1.2.0-linux-x86_64.AppImage |
| Linux AppImage (ARM64) | floppybootcd-1.2.0-linux-arm64.AppImage |
| Debian / Ubuntu (x86_64) | floppybootcd-1.2.0-linux-x86_64.deb |
| Debian / Ubuntu (ARM64) | floppybootcd-1.2.0-linux-arm64.deb |
| Fedora / RHEL (x86_64) | floppybootcd-1.2.0-linux-x86_64.rpm |
| Fedora / RHEL (ARM64) | floppybootcd-1.2.0-linux-arm64.rpm |
| Linux tarball (x86_64) | floppybootcd-1.2.0-linux-x86_64.tar.gz |
| Linux tarball (ARM64 / Raspberry Pi 3-5) | floppybootcd-1.2.0-linux-arm64.tar.gz |
Or upgrade in place: uv tool upgrade floppybootcd.
🙏 If you tried v1.1.0 – v1.1.4 and gave up
The crash was real, the first attempt to fix it was wrong, and the recovery shipped one bug at a time before this consolidation. v1.2.0 has been traced against the actual PySide6 thread-affinity rules with citations and end-to-end verification — and the WinImage support people had been asking for is in.
What's Changed
Full Changelog: v1.1.4...v1.2.0