Load images from 8-bit computer tapes
Project description
pillow_zx_spectrum
Pillow loaders for ZX Spectrum loading screens, extracted from tape, disk and snapshot files in the wild.
Open a tape, disk image, or snapshot with Image.open(...) and Pillow
gives you a 256×192 RGB image with the original Spectrum colours. Files
that contain multiple screens (multi-load games, multi-side disks)
expose them as Pillow frames via seek() / n_frames.
Install
pip install pillow_zx_spectrum
Usage
from PIL import Image
import pillow_zx_spectrum # registers the plugins on import
img = Image.open("Glug Glug (1984)(CRL).tap")
img.save("loading-screen.png")
print(img.size) # (256, 192)
print(img.info["pixel_aspect_ratio"]) # (1, 1)
Multi-screen containers (disks, multi-load tapes) expose every screen they hold. Iterate with the stock Pillow helper:
from PIL import ImageSequence
img = Image.open("Moonwalker (Erbe).dsk")
print(img.n_frames) # e.g. 2
for i, frame in enumerate(ImageSequence.Iterator(img)):
frame.save(f"moonwalker.{i}.png")
Supported formats
Tape
| Ext | Format | Notes |
|---|---|---|
.tap |
TAP | Plain ROM-loader block stream |
.tzx |
TZX | Versioned tape with timing/meta blocks; we extract from standard (0x10) and turbo-speed (0x11) data blocks. Custom-loader pure-data blocks (SpeedLock / BleepLoad / Alkatraz, block 0x14) are skipped — they need per-protection decoders. |
.scr |
SCREEN$ | Raw 6912-byte screen dump |
Disk
| Ext | Format | Notes |
|---|---|---|
.dsk |
CPC DSK | Spectrum +3 / Amstrad CPC, both standard and "Extended" variants. CP/M file system parsed (handles fragmented allocation). Auto-detects single/double-sided and various reserved-track conventions. |
.scl |
TR-DOS packed | "SINCLAIR" magic, compact distribution format used in Russian-speaking scene |
.trd |
TR-DOS raw | Sector-by-sector TR-DOS floppy dump; tolerant of truncated images |
.mgt |
DISCiPLE / +D | Side-interleaved 80-track disk for the MGT DISCiPLE & +D interfaces. CODE/SCREEN$ files reassembled by following the per-sector chain. |
.d40, .d80 |
Didaktik MDOS | Czechoslovak Didaktik D40/D80 floppy. MDOS file system with non-standard FAT12 packing (the 12-bit entries' high nibbles are byte-swapped relative to MS FAT12). Auto-detects geometry from the boot sector "SDOS" marker. Most TOSEC Czech games use packed loaders so plain SCREEN$ extraction is rare. |
Microdrive
| Ext | Format | Notes |
|---|---|---|
.mdr |
Microdrive cart | Sector-by-sector dump of a Spectrum Microdrive cartridge (543-byte sectors with mod-255 checksums). Files reconstructed by gathering all records belonging to a filename and stripping the inline 9-byte header. PRINT# files are not handled. |
Snapshot
| Ext | Format | Notes |
|---|---|---|
.sna |
SNA | Classic 48K (49179 bytes) / 128K (131103 bytes) snapshot |
.z80 |
Z80 | Gerton Lunter format (v1, v2, v3); 48K and 128K with bank decompression |
.szx |
SZX / ZX-State | Spectaculator's modern chunked snapshot, with zlib-compressed RAM pages |
.slt |
SLT | "Super Level Loader" — a Z80 snapshot with a table of additional screens (each becomes a frame) |
For 128K snapshots the shadow screen (bank 7) is exposed as an extra frame when it's distinct from the main screen.
How it works
Every container is reduced to a sequence of load events —
(addr, body, name, kind) tuples — and walked into a 64K Spectrum RAM
image. After each write the pipeline harvests two kinds of candidate:
- Direct — if the event's body is exactly 6912 bytes (the SCREEN$ shape), it might be a screen, regardless of its load address.
- RAM snapshot — slice
$4000-$5AFFafter the write and emit it if the screen area now contains a new picture (catches screens loaded as part of larger CODE blocks, screens overwritten by later loads, etc.)
Candidates are then ranked by filename hint (SCR / PIC / TITL /
LOAD / INTRO...) → canonical $4000 address → other; bodies that
clearly aren't screens (random attribute distribution, all-FLASH cells)
are dropped. The same pipeline runs for every format.
What we don't load
- Sampled-audio tapes (
.csw,.wav,.mp3) — these encode the loader's pulse train, decoding requires a Z80 emulator running the Spectrum ROM - Copy-protected disks (
.ipf) — needs the CAPS/SPS library - Custom-loader tapes (SpeedLock 7, Alkatraz, BleepLoad, ...) — non-standard timing; the screen lives inside TZX 0x14 pure-data blocks that we skip rather than implement per-protection decoders for
- 16K Spectrum games — no SCREEN$ saved before the program
- BASIC-only programs that draw their screen at runtime
These cases produce UnidentifiedImageError ("recognised the format,
nothing to extract") rather than a misleading blank frame.
License
WTFPL with one additional clause:
- Don't blame me.
Do what you like, but you're to blame.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pillow_zx_spectrum-0.1.0.tar.gz.
File metadata
- Download URL: pillow_zx_spectrum-0.1.0.tar.gz
- Upload date:
- Size: 40.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
832b758a86e677105946202a4be911275214705bfe2a274bbfdd8b6108bb3297
|
|
| MD5 |
758213129b5fc54cff0d3fce237627e8
|
|
| BLAKE2b-256 |
40cc5028978705e725ed5a5efb43394e2028102643a3b351bce1f0fd5e7bbbad
|
File details
Details for the file pillow_zx_spectrum-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pillow_zx_spectrum-0.1.0-py3-none-any.whl
- Upload date:
- Size: 54.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fff08b8821444eb419bfaf39d7dd92a761272848ae729a14f2c698147f8055ee
|
|
| MD5 |
9b0cd5af0f5fb4b1a2ac0bd86599cf43
|
|
| BLAKE2b-256 |
690121720c80478a3b57f8025d8fd7248a7d332c114f450d92432821a83a7df5
|