Skip to main content

oaknut-adfs

PyPI version CI Python versions License: MIT

A Python library for reading, writing, and creating Acorn ADFS (Advanced Disc Filing System) disc images — the hierarchical filing system used by the BBC Master, Acorn Archimedes, and RISC OS.

oaknut-adfs opens ADFS floppy and hard-disc images to browse the directory tree, read and write files and directories, inspect and edit RISC OS metadata (filetypes, datestamps, access), and create new formatted discs — from Python, with a pathlib-inspired API.

Looking for DFS? The flat-catalogue Disc Filing System of the BBC Micro is a separate filing system in the sibling oaknut-dfs package (from oaknut.dfs import DFS). For a unified command-line tool across DFS, ADFS, AFS, ROMFS and ZIP, see oaknut-disc.

Part of the oaknut monorepo.

Supported formats

ADFS evolved across three machine generations. oaknut-adfs reads, writes, and creates every standard floppy format and the FileCore hard-disc layout:

Format Map Directory Capacity Sector Zones Notes
S Old Old 160 KB 256 B 40-track single-sided; ADFS on the BBC B / Electron
M Old Old 320 KB 256 B 80-track single-sided
L Old Old 640 KB 256 B 80-track double-sided
D Old New 800 KB 1024 B Arthur / the BBC Master 512
E New New 800 KB 1024 B 1 double density; RISC OS
E+ New Big 800 KB 1024 B 1 RISC OS 4 — long filenames
F New New 1600 KB 1024 B 4 high density; RISC OS
F+ New Big 1600 KB 1024 B 4 RISC OS 4 — long filenames
G New New 3200 KB 1024 B 8 extra-high density; rarely used
G+ New Big 3200 KB 1024 B 8 RISC OS 4 — long filenames; rarely used
Hard disc New New / Big multi-megabyte 256 / 512 B many Archimedes / RISC OS FileCore

The map and directory are independent axes; the format letter is shorthand for a particular pairing.

Allocation maps

  • Old map — a free-space map in sectors 0–1, listing free fragments as (start, length) pairs, guarded by two additive checksums. Files occupy contiguous sectors. Used by S, M, L and D.
  • New map (FileCore) — a fragmented allocation scheme. A disc record at offset 0x04 describes the geometry; a zoned allocation bitmap tracks variable-length fragments addressed by a fragment id plus a sector offset. This is what lifts the size ceiling of the old map and allows the multi-zone F and G formats and large hard discs. Used by E, F, G and their + variants.

Directory formats

  • Old directory (Hugo) — a fixed 1280-byte block, up to 47 entries, 10-character names. Used by S, M and L.
  • New directory (Hugo / Nick) — a fixed 2048-byte block, 10-character names, carrying the 32-bit RISC OS load/exec fields. Used by D, E, F and G.
  • Big directory (SBPr / oven) — a variable-size block with a packed name heap, supporting filenames of up to 255 characters. Used by the + formats (E+, F+, G+).

Hard-disc images

New-map hard discs are read, written, and created in both common on-disc layouts:

  • .hdf — RPCEmu / Arculator IDE images, where FileCore numbers sectors from low_sector, placing disc address 0 at a 0x200 offset. Detected and handled by content.
  • .dat / .dsc — a raw image with a 22-byte SCSI geometry sidecar; the New-map geometry is read from the disc record, so the sidecar is optional.

Metadata

RISC OS is a 32-bit system, and its per-file metadata lives in the load and execution address fields:

  • Filetypes and datestamps. When the top 12 bits of the load address are &FFF, the field encodes a 12-bit filetype and a 40-bit datestamp (centiseconds since 1900) rather than genuine addresses. oaknut-adfs stores both the raw fields and, through the oaknut.filesystem capability layer, the decoded filetype and datestamp.
  • Access bits. Owner and public read/write, plus the lock (L) bit, via the shared oaknut-file Access type.

Content-based identification (via oaknut.filesystem) recognises every map and directory combination above, so a disc need not be labelled by extension.

Installation

uv add oaknut-adfs        # or: pip install oaknut-adfs

oaknut-adfs works with any PEP 517 build front-end and package manager; the examples use uv.

Usage

Opening and reading

The format is auto-detected from the image content (and, where content is ambiguous, the file extension). Pass a format only to override detection.

from oaknut.adfs import ADFS

with ADFS.from_file("RISCOS.adf") as adfs:
    print(adfs.title)

    # Navigate with a pathlib-inspired API; "$" is the root.
    for entry in (adfs.root / "$").iterdir():
        s = entry.stat()
        print(f"{entry.name:12s} {s.length:7d}  load={s.load_address:08X}")

    data = (adfs.root / "$" / "!Boot").read_bytes()

from_file opens the image read/write when host permissions allow; pass read_only=True to guarantee a shared image cannot be modified.

Creating a disc

Every floppy format has a module constant. Pass it to create_file:

from oaknut.adfs import ADFS, ADFS_E, ADFS_F_PLUS

# An 800 KB New-map E disc.
with ADFS.create_file("disc.adf", ADFS_E, title="RISCOS") as adfs:
    (adfs.root / "$.ReadMe").write_bytes(
        b"oaknut-adfs demo", load_address=0xFFFFFF00, exec_address=0x00000000
    )
    (adfs.root / "$.Apps").mkdir()

# A 1600 KB F+ disc, whose Big directories allow long filenames.
with ADFS.create_file("big.adf", ADFS_F_PLUS, title="Extended") as adfs:
    (adfs.root / "$.AVeryLongFileNameIndeed").write_bytes(b"...")

Hard-disc images

from oaknut.adfs import ADFS

# Size a New-map hard disc from a capacity; big_directories=True selects
# the Big directory (E+/F+) layout.
adfs = ADFS.create_new_map_hard_disc("40MB", title="IDEDisc", big_directories=True)

Validating structure

validate() walks the whole disc — map, zone checks, and directory tree — and returns a list of structural errors (empty when the disc is sound):

with ADFS.from_file("disc.adf") as adfs:
    errors = adfs.validate()
    assert errors == []

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-adfs/tests # this package's tests
uv run ruff check                        # lint

Architecture

A layered design, dependencies flowing strictly downward:

ADFS (adfs.py)                     user-facing ADFS / ADFSPath / ADFSStat
  ↓
directory.py  ←→  free_space_map.py  ←→  new_map.py
  (Old/New/Big dirs)  (old map)          (FileCore new map)
  ↓
UnifiedDisc / Surface / SectorsView   (from oaknut-discimage)
  • adfs.py — the user-facing ADFS, ADFSPath, ADFSStat; format detection, the format constants, and disc creation.
  • directory.py — the Old, New, and Big directory formats (parse, serialise, and their respective check-byte algorithms).
  • free_space_map.py — the old-map free-space map and its checksums.
  • new_map.py — the FileCore disc record, zoned allocation bitmap, fragment allocation and sharing, and blank-image formatting.
  • filesystem.py — the oaknut.filesystem adapter: content identification, the mount, and the Filetyped / Datestamped capabilities.

Sector-level access (Surface, SectorsView, UnifiedDisc) lives in oaknut-discimage; metadata and the acorn codec in oaknut-file; BBC BASIC (de)tokenisation in oaknut-basic.

oaknut-adfs and oaknut-dfs are independent siblings: from oaknut.dfs import ADFS does not work — ADFS lives in oaknut.adfs.

Further reading

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

oaknut_adfs-12.15.1.tar.gz (126.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

oaknut_adfs-12.15.1-py3-none-any.whl (78.2 kB view details)

Uploaded Python 3

File details

Details for the file oaknut_adfs-12.15.1.tar.gz.

File metadata

  • Download URL: oaknut_adfs-12.15.1.tar.gz
  • Upload date:
  • Size: 126.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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}

File hashes

Hashes for oaknut_adfs-12.15.1.tar.gz
Algorithm Hash digest
SHA256 9a415b2816055a1670fedac41ed6d02f40654375d09ae6eea32dbffd498bc77a
MD5 bb526ebe0367e525b2ab186982497749
BLAKE2b-256 f1f9a2cfee095ee75bd3df85f0763b6ef13264a2bb7d380d2f17eadafa40b8ec

See more details on using hashes here.

File details

Details for the file oaknut_adfs-12.15.1-py3-none-any.whl.

File metadata

  • Download URL: oaknut_adfs-12.15.1-py3-none-any.whl
  • Upload date:
  • Size: 78.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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}

File hashes

Hashes for oaknut_adfs-12.15.1-py3-none-any.whl
Algorithm Hash digest
SHA256 07c6bfce33e38d155d0f2c40c2df8e6ff4dab7aa2f284eb0852d4cbd9ceda30b
MD5 01f37e2318f86f24a42f905307be1d8d
BLAKE2b-256 92db28f366b38673abe4020f1630c9d30a914aed386fc5603109930ed8ddafdf

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

12.15.1 This release

2 files

12.15.0

2 files

12.14.1

2 files

12.14.0

2 files

12.13.1

2 files

12.12.0

2 files

12.11.0

2 files

12.10.1

2 files

12.10.0

2 files

12.9.0

2 files

12.8.2

2 files

12.8.1

2 files

12.8.0

2 files

12.7.2

2 files

12.7.1

2 files

12.7.0

2 files

12.6.2

2 files

12.6.1

2 files

12.6.0

2 files

12.5.3

2 files

12.5.2

2 files

12.5.1

2 files

12.5.0

2 files

12.4.1

2 files

12.4.0

2 files

12.3.0

2 files

12.2.0

2 files

12.1.0

2 files

12.0.1

2 files

12.0.0

2 files

11.2.0

2 files

11.1.0

2 files

11.0.2

2 files

11.0.1

2 files

11.0.0

2 files

10.7.0

2 files

10.6.0

2 files

10.5.0

2 files

10.4.0

2 files

10.3.0

2 files

10.2.0

2 files

10.1.0

2 files

10.0.5

2 files

10.0.4

2 files

10.0.3

2 files

10.0.2

2 files

10.0.1

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page