{"id":47817151,"url":"https://github.com/acscpt/beebtools","last_synced_at":"2026-04-05T20:00:32.553Z","repository":{"id":347890091,"uuid":"1195604038","full_name":"acscpt/beebtools","owner":"acscpt","description":"A Python tool/library for working with BBC Micro DFS images and BBC BASIC detokenization","archived":false,"fork":false,"pushed_at":"2026-04-04T19:22:22.000Z","size":555,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-04T20:29:58.430Z","etag":null,"topics":["bbc","bbc-basic","bbc-dfs","bbc-micro","detokenization"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/acscpt.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-29T21:27:30.000Z","updated_at":"2026-04-04T19:22:25.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/acscpt/beebtools","commit_stats":null,"previous_names":["acscpt/beebtools"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/acscpt/beebtools","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acscpt%2Fbeebtools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acscpt%2Fbeebtools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acscpt%2Fbeebtools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acscpt%2Fbeebtools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/acscpt","download_url":"https://codeload.github.com/acscpt/beebtools/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acscpt%2Fbeebtools/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31448216,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T15:22:31.103Z","status":"ssl_error","status_checked_at":"2026-04-05T15:22:00.205Z","response_time":75,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bbc","bbc-basic","bbc-dfs","bbc-micro","detokenization"],"created_at":"2026-04-03T18:46:36.106Z","updated_at":"2026-04-05T20:00:32.537Z","avatar_url":"https://github.com/acscpt.png","language":"Python","readme":"# beebtools\n\n[![PyPI](https://img.shields.io/pypi/v/beebtools.svg)](https://pypi.org/project/beebtools/)\n[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n[![Tests](https://github.com/acscpt/beebtools/actions/workflows/tests.yml/badge.svg)](https://github.com/acscpt/beebtools/actions/workflows/tests.yml)\n\nA Python tool for working with BBC Micro DFS and ADFS disc images.\n\n`beebtools` reads DFS (`.ssd`/`.dsd`) and ADFS (`.adf`/`.adl`) disc images.\nIt can list catalogues, extract and detokenize BBC BASIC programs to a more\nhuman-readable (and text editor friendly) format, and includes a pretty-printer\nthat makes dense BBC BASIC code more legible.\n\n## Disc images\n\nBBC Micro software is widely preserved as disc images - raw sector-by-sector\ndumps of original floppy discs. `beebtools` supports two filing systems:\n\n- **DFS** (Disc Filing System) - `.ssd` (single-sided) and `.dsd` (double-sided\n  interleaved) images. Flat catalogue with up to 31 files per side, single-character\n  directory prefixes (`$`, `T`, etc.).\n\n- **ADFS** (Advanced Disc Filing System) - `.adf` (single-sided) and `.adl`\n  (double-sided) images. Hierarchical directory tree with up to 47 entries per\n  directory, full path names like `$.GAMES.ELITE`. Supports ADFS-S (160K),\n  ADFS-M (320K), and ADFS-L (640K) old-map disc images.\n\n`beebtools` reads catalogues from both formats and can list them in a\nhuman-readable table, sorted by name, catalogue order, or file size.\n\nFiles are extracted by name (`T.MYPROG`, `$.GAMES.ELITE`) or by bare name when\nunambiguous. On a double-sided `.dsd` image both sides are catalogued; if the\nsame bare name appears on both sides, `beebtools` tells you and asks you to be\nspecific. Bulk extraction (`-a`) pulls every file off the disc at once.\n\n## Programs: BBC BASIC and binary files\n\nMost files you will want to look at on a BBC Micro disc are BBC BASIC programs.\nThey are not stored as text. The BBC Micro's BASIC ROM tokenizes programs before\nsaving them: keywords like `PRINT`, `GOTO`, and `FOR` are replaced with single\nbytes in the range 0x80-0xFF, `GOTO` and `GOSUB` targets are encoded as compact\n3-byte line-number references, and the whole thing is written as a sequence of\nbinary line records with no human-readable structure.\n\nBinary files (machine code, data, sound samples) are stored as raw bytes and\nextracted as-is.\n\nFor BASIC files, `beebtools` does two things in sequence when extracting:\n\n1. **Detokenize** - decode the binary line records back to `LIST`-style text,\n   expanding keyword tokens, decoding line-number references, and handling\n   `REM` and `DATA` tails correctly (they are literal ASCII and must not be\n   expanded).\n\n2. **Pretty-print** (optional, `--pretty`) - add operator spacing to the\n   raw detokenized text. BBC BASIC stores only the spaces the programmer\n   explicitly typed, so code like `IFx\u003e100THENx=0:y=0` is normal. The\n   pretty-printer adds spaces around operators and punctuation while leaving\n   string literals, `REM` tails, and `DATA` tails completely untouched.\n\n   `beebtools` also handles anti-listing traps, a simply constructed statement\n   that was embedded within a BASIC program.  A line starting with `*|` followed\n   by `VDU 21` (disable output) bytes.  A simple and effective  copy-protection trick.\n   The pretty-printer converts `*|` statements to `REM *|` and strips the\n   control characters so the program is readable.\n\nWhen creating images, `beebtools` performs the reverse - plain-text BASIC (as produced\nby step 1 or 2) is retokenized back to the binary format the BBC Micro\nexpects.  The anti-listing trick is not reversed and re-injected into the program\nthough in this case.\n\n## Features\n\n- Read DFS catalogues from `.ssd` and `.dsd` disc images\n\n- Read ADFS catalogues from `.adf` and `.adl` disc images (old-map, Hugo directories)\n\n- Extract individual files by name (`T.MYPROG`, `$.GAMES.ELITE`, or bare `MYPROG`)\n\n- Bulk-extract everything from a disc image at once\n\n- Detokenize BBC BASIC II programs to `LIST`-style plain text\n\n- Retokenize plain-text BASIC back to binary - enabling a full\n  detokenize-edit-retokenize workflow\n\n- Pretty-printer: add operator spacing to make terse BASIC readable\n  - Anti-listing trap detection: neutralise copy-protection `*|` traps\n\n- Star command awareness: `*SCUMPI` is passed through verbatim, no false spacing\n\n- `.inf` sidecar format support: parse and produce the standard community\n  interchange format for preserving DFS/ADFS file metadata alongside extracted files\n\n- Create, modify, and build disc images from the command line or as a library\n\n- Zero dependencies - pure Python 3.8+, single package\n\n## Installation\n\n```bash\npip install beebtools\n```\n\nFor development (installs `pytest` and uses an editable install):\n\n```bash\ngit clone https://github.com/acscpt/beebtools\ncd beebtools\npython -m venv .venv\nsource .venv/bin/activate   # Windows: .venv\\Scripts\\activate\npip install -e \".[dev]\"\n```\n\n## Commands\n\n`beebtools` provides commands for inspecting, extracting, and building disc\nimages. All commands work with both DFS and ADFS images. Each command has its\nown detailed reference page.\n\n| Command | Description |\n| --- | --- |\n| [`cat`](https://github.com/acscpt/beebtools/blob/main/docs/commands/cat.md) | List disc catalogue with file types and metadata |\n| [`search`](https://github.com/acscpt/beebtools/blob/main/docs/commands/search.md) | Search BASIC source for a text pattern or regex |\n| [`extract`](https://github.com/acscpt/beebtools/blob/main/docs/commands/extract.md) | Extract a single file or bulk-extract all files |\n| [`create`](https://github.com/acscpt/beebtools/blob/main/docs/commands/create.md) | Create a blank formatted disc image |\n| [`add`](https://github.com/acscpt/beebtools/blob/main/docs/commands/add.md) | Add a file to an existing disc image |\n| [`delete`](https://github.com/acscpt/beebtools/blob/main/docs/commands/delete.md) | Delete a file from a disc image |\n| [`build`](https://github.com/acscpt/beebtools/blob/main/docs/commands/build.md) | Build a disc image from files with `.inf` sidecars |\n\n## Usage\n\n```bash\n# List what is on a disc image\nbeebtools cat mydisc.dsd\n\n# Extract and detokenize a BASIC program\nbeebtools extract mydisc.dsd T.MYPROG\n\n# Extract with operator spacing added\nbeebtools extract mydisc.dsd T.MYPROG --pretty\n\n# Extract everything from a double-sided disc\nbeebtools extract mydisc.dsd -a --pretty -d output/\n\n# Extract everything with .inf sidecars preserving file metadata\nbeebtools extract mydisc.dsd -a --inf -d output/\n\n# List an ADFS disc catalogue\nbeebtools cat game.adf\n\n# Extract a file from an ADFS disc by full path\nbeebtools extract game.adf $.GAMES.ELITE --pretty\n\n# Bulk-extract an ADFS disc\nbeebtools extract game.adf -a -d output/\n\n# Create a blank disc image\nbeebtools create blank.ssd --title \"MY DISC\" --boot EXEC\n\n# Add a file to an existing image\nbeebtools add mydisc.ssd loader.bin --name $.LOADER --load 1900 --exec 1900\n\n# Add a file using its .inf sidecar for metadata\nbeebtools add mydisc.ssd loader.bin --inf\n\n# Delete a file from an image\nbeebtools delete mydisc.ssd $.LOADER\n\n# Build a disc image from a directory of files with .inf sidecars\nbeebtools build output/ rebuilt.ssd --title \"REBUILT\"\n\n# Create a blank ADFS image (320K)\nbeebtools create blank.adf -t 80 --title \"MY ADFS\" --boot RUN\n\n# Add a file to an ADFS image with a hierarchical path\nbeebtools add mydisc.adf loader.bin --name $.GAMES.LOADER --load 1900 --exec 1900\n\n# Build an ADFS image from a directory tree\nbeebtools build output/ rebuilt.adl --title \"REBUILT\"\n```\n\n## Pretty-printer\n\nWhen extracting BASIC files from a disc image, the `--pretty` flag adds\noperator spacing to make the dense tokenized code more readable.\n\n```basic\n  100 IFx\u003e100ORy\u003c0THENx=0:y=0\n  110 FORi=1TO8:s=s+x*x:NEXTi\n  120 SOUND1,-15,s,5:IFs\u003e9999THENs=0\n```\n\nWith `--pretty`:\n\n```basic\n  100 IFx \u003e 100ORy \u003c 0THENx = 0 : y = 0\n  110 FORi = 1TO8 : s = s + x * x : NEXTi\n  120 SOUND1, -15, s, 5 : IFs \u003e 9999THENs = 0\n```\n\nSee [docs/pretty-printer.md](https://github.com/acscpt/beebtools/blob/main/docs/pretty-printer.md) for the full list of\nspacing rules and anti-listing trap handling.\n\n## Using as a library\n\n```python\nfrom beebtools import openImage, detokenize, tokenize, prettyPrint\n\n# openImage auto-detects DFS (.ssd/.dsd) or ADFS (.adf/.adl)\nwith openImage(\"mydisc.dsd\") as image:\n    for side in image:\n        for entry in side.readCatalogue():\n            if entry.isBasic:\n                data = side.readFile(entry)\n                lines = prettyPrint(detokenize(data))\n                print(\"\\n\".join(lines))\n\n# Retokenize edited plain text back to binary\nedited_lines = [\"   10PRINT\\\"HELLO\\\"\", \"   20END\"]\nbinary = tokenize(edited_lines)\n```\n\nSee [docs/library.md](https://github.com/acscpt/beebtools/blob/main/docs/library.md) for creating disc images, building from\n`.inf` sidecars, and working with the `.inf` format programmatically.\n\n## Supported formats\n\n| Format | Filing system | Description |\n| --- | --- | --- |\n| `.ssd` | DFS | Single-sided 40 or 80 track |\n| `.dsd` | DFS | Double-sided interleaved |\n| `.adf` | ADFS | Single-sided (ADFS-S 160K, ADFS-M 320K) |\n| `.adl` | ADFS | Double-sided (ADFS-L 640K) |\n\nDFS: both 40-track and 80-track images are supported. Watford DFS extended\ncatalogues (62-file discs) are not supported.\n\nADFS: old-map (small directory, \"Hugo\" format) images are supported for both\nreading and writing. New-map large-directory formats (ADFS-D/E/F/G) are not\nsupported.\n\n## Documentation\n\nSee the [docs/](https://github.com/acscpt/beebtools/blob/main/docs/README.md) folder for full command reference, pretty-printer\ndetails, and library API guide.","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facscpt%2Fbeebtools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Facscpt%2Fbeebtools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facscpt%2Fbeebtools/lists"}