oaknut-dfs
A Python library for reading, writing, and creating Acorn DFS floppy disc images, as used by the BBC Micro.
With oaknut-dfs you can open DFS floppy images (SSD/DSD) to browse the catalogue, read and write files, inspect metadata, and create new formatted discs — all from Python, with a pathlib-inspired API.
Looking for ADFS? The hierarchical Advanced Disc Filing System lives in the sibling
oaknut-adfspackage (from oaknut.adfs import ADFS). DFS and ADFS are independent filing systems and independent packages. For a unified command-line tool across DFS, ADFS, AFS, ROMFS and ZIP, seeoaknut-disc.
Part of the oaknut monorepo.
Supported formats
- Acorn DFS — 40-track and 80-track, single-sided (SSD) and double-sided (DSD)
- Watford DFS — extended catalogue supporting up to 62 files
- DSD interleaving — both interleaved and sequential double-sided layouts
Acorn load/exec addresses, the lock bit, and the BBC Micro character set
(the acorn text codec, £, ¦) are handled through the shared
oaknut-file
metadata layer.
Installation
uv add oaknut-dfs # or: pip install oaknut-dfs
oaknut-dfs is a standard Python package installable with any package
manager; the examples use uv.
Usage
Opening and reading files
The disc format is auto-detected from the file extension and size; pass a format explicitly only to override detection.
from oaknut.dfs import DFS
with DFS.from_file("Zalaga.ssd") as dfs:
print(dfs.title) # 'ZALAG-L'
# Navigate with a pathlib-inspired API.
for entry in dfs.root / "$":
s = entry.stat()
print(f"{entry.name:10s} {s.length:6d} load={s.load_address:08X}")
# Read file data.
data = (dfs.root / "$" / "ZALAGA").read_bytes()
Creating a new disc
from oaknut.dfs import DFS, ACORN_DFS_80T_SINGLE_SIDED
with DFS.create_file("demo.ssd", ACORN_DFS_80T_SINGLE_SIDED, title="DEMO") as dfs:
(dfs.root / "$.HELLO").write_bytes(b"Hello, World!", load_address=0x1900)
(dfs.root / "$.README").write_bytes(b"oaknut-dfs demo disc")
Double-sided discs (DSD)
A DSD image holds two independent sides, each with its own catalogue —
mirroring the BBC Micro, where the sides were accessed as separate drives
(*DRIVE 0 and *DRIVE 2). Select a side with the side= argument.
from oaknut.dfs import DFS
with DFS.from_file("game.dsd") as side0:
print(side0.title)
with DFS.from_file("game.dsd", side=1) as side2:
print(side2.title)
Walking the disc
DFS directories ($, A–Z) appear as children of a virtual root:
with DFS.from_file("disc.ssd") as dfs:
for dirpath, dirnames, filenames in dfs.root.walk():
for name in filenames:
print(dirpath / name)
Development
The package is developed in the oaknut workspace. From the repository root:
uv sync # install all workspace members editable
uv run pytest packages/oaknut-dfs/tests # this package's tests
uv run ruff check # lint
Architecture
A layered design, dependencies flowing strictly downward (every module imports only from the layer below):
DFS (dfs.py) user-facing DFS / DFSPath / DFSStat
↓
CataloguedSurface (catalogued_surface.py)
↓
Catalogue ABC (catalogue.py) ← AcornDFSCatalogue / WatfordDFSCatalogue
↓
Surface / SectorsView (from oaknut-discimage)
dfs.py— user-facingDFS,DFSPath,DFSStat; methods mirror the DFS star commands (load,save,delete,rename,lock, …).catalogue.py— the catalogue ABC (Catalogue,FileEntry,DiscInfo): 31-file cap, single-character directories, 7-char names.acorn_dfs_catalogue.py/watford_dfs_catalogue.py— the Acorn (sectors 0–1, 31 files) and Watford (sectors 0–3, 62 files) catalogues.catalogued_surface.py— pairs aSurfacewith aCatalogue.formats.py— DFS and Watford disc-format constants.
Sector-level access (Surface, SectorsView, UnifiedDisc) lives in
oaknut-discimage;
metadata and the acorn codec in oaknut-file; BBC BASIC
(de)tokenisation (for DFSPath.read_basic / write_basic) in
oaknut-basic.
References
- Acorn DFS disc format — BeebWiki specification for the catalogue layout.
- Disc Filing System — Wikipedia overview of DFS and its variants.
- DiscImageManager — Gerald Holdsworth's reference for DFS and other Acorn formats.
- INF file format —
the
.infsidecar metadata format.
License
MIT — see LICENSE.
Metadata
Release files for oaknut-dfs 12.16.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| oaknut_dfs-12.16.0.tar.gz | 103.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| oaknut_dfs-12.16.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 155.4 kB
Release files / oaknut_dfs-12.16.0.tar.gz
| Download URL | oaknut_dfs-12.16.0.tar.gz |
|---|---|
| Size | 103.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fa6c4947ddca4d0a2c0acec11d7e2c270a36652863cd14ca894e5efadf4467ae
|
|
BLAKE2b-256 checksum How to use checksums |
397e393e72244299ed740f71cc2ddcda1ab49e5c8c79032bd4974a67541758fd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / oaknut_dfs-12.16.0-py3-none-any.whl
| Download URL | oaknut_dfs-12.16.0-py3-none-any.whl |
|---|---|
| Size | 51.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1a0006330a2acba6dcca9323552242ad81486c18bd0001f7b0078060f6f65f69
|
|
BLAKE2b-256 checksum How to use checksums |
ecda469ad152693cf7ce8a78016184279aa282b0a2399f25c14d983fa5b60601
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|