Skip to content

Commit 6a379a3

Browse files
committed
Merge feature/max-inf: complete .inf sidecar support (v0.9.0)
Full access byte, directory .inf, CRC16/CRC32, flat extraction layout as default, robust parser with warnings, real-world smoke tests.
2 parents b8a8c32 + b2c9144 commit 6a379a3

16 files changed

Lines changed: 1927 additions & 193 deletions

CHANGELOG.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,96 @@ All notable changes to this project will be documented here.
55
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66
Versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
## [0.9.0] - 2026-04-13
11+
12+
### Added
13+
14+
- Directory-level `.inf` sidecars on extract. `extractAll` now writes a
15+
`$.inf` alongside the root directory (or per-side `$.inf` for DSD)
16+
carrying `TITLE=` and `OPT=` extra-info keys. Formats with
17+
subdirectories also get a `.inf` per directory entry.
18+
19+
- `formatDirectoryInf()` helper for emitting directory-level `.inf`
20+
lines in stardot spec syntax 1 (zeroed hex fields + extra-info keys).
21+
22+
- `buildImage()` reads `$.inf` as the source of truth for disc title
23+
and boot option. Explicit `title`/`boot_option` arguments warn when
24+
a `$.inf` is present; pass `force=True` to override.
25+
26+
- CLI `--force` flag on the `build` subcommand to override `$.inf`
27+
disc metadata with explicit `--title`/`--boot` values.
28+
29+
- CRC16 and CRC32 checksums emitted in `.inf` sidecars on extract.
30+
Validated on build with a warning on mismatch.
31+
32+
- Flat extraction layout. Files are named using the full Acorn path
33+
(e.g. `$.BOOT.bas`, `T.DATA.bin`, `$.GAMES.ELITE.bin`). DSD images
34+
use `side0/`/`side1/` subdirectories with flat contents per side.
35+
36+
- CLI `--mkdirs` flag on the `extract -a` subcommand to opt into
37+
the old hierarchical directory layout.
38+
39+
- CLI `--no-inf` flag on `extract -a` to suppress `.inf` sidecars.
40+
41+
### Deprecated
42+
43+
- `--inf` flag on `extract -a` is now the default and emits a
44+
`DeprecationWarning`. It will be removed in a future release.
45+
46+
### Changed (breaking)
47+
48+
- **Default extract behaviour changed.** `extractAll()` now writes a
49+
flat directory of files named by their full Acorn path (e.g.
50+
`$.BOOT.bas`) and writes `.inf` sidecars by default. Previously
51+
it created subdirectories per DFS directory character and required
52+
`--inf` to emit sidecars. Use `--mkdirs` (CLI) or
53+
`layout="hierarchical"` (library) for the old directory layout;
54+
use `--no-inf` (CLI) or `write_inf=False` (library) to suppress
55+
sidecars.
56+
57+
- `InfData.access_byte` replaces the old `locked` boolean field. The
58+
full 8-bit access byte is preserved through parse, format, and
59+
round-trip. `locked` is now a read-only property (bit 3 of
60+
`access_byte`).
61+
62+
- `DiscEntry` ABC gains an `accessByte` property (default: 0x08 if
63+
locked, 0x00 otherwise). Format engines with richer access bits
64+
override it to return the full byte.
65+
66+
- `formatInf()` now percent-encodes spaces in extra-info values so
67+
they survive whitespace-delimited tokenization on re-parse.
68+
`parseInf()` percent-decodes extra-info values.
69+
70+
- `buildImage()` signature: `title` and `boot_option` are now
71+
`Optional` (default `None`), distinguishing "not set" from an
72+
explicit empty/OFF value. New `force` parameter.
73+
74+
### Fixed
75+
76+
- `.inf` parser now warns on unrecognised tokens instead of silently
77+
discarding remaining fields. Malformed tokens are skipped so that
78+
valid fields further along the line are still captured.
79+
80+
- `InfData.crc` property now emits `BeebToolsWarning` on unparseable
81+
hex, consistent with `startSector`. Previously returned `None`
82+
silently.
83+
84+
- New `InfData.crc32` property with the same warn-on-bad-value
85+
pattern. Replaces inline `try/except` in build validation.
86+
87+
- `_decodePercentEscapes` warns on invalid or truncated `%XX`
88+
sequences instead of silently treating them as literals.
89+
90+
- Bare `$` in root directory `.inf` (Disc Image Manager format) now
91+
correctly parses as `directory="$", name=""` instead of
92+
`name="$"`.
93+
94+
- Extra-info values containing spaces were silently truncated on
95+
round-trip because `formatInf` emitted them unencoded and the
96+
tokenizer split on whitespace.
97+
898
## [0.8.0] - 2026-04-12
999

10100
### Added

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ beebtools extract mydisc.dsd T.MYPROG --pretty
164164
beebtools extract mydisc.dsd -a --pretty -d output/
165165

166166
# Extract everything with .inf sidecars preserving file metadata
167-
beebtools extract mydisc.dsd -a --inf -d output/
167+
beebtools extract mydisc.dsd -a -d output/
168168

169169
# Preserve teletext control codes in BASIC strings (lossless UTF-8)
170170
beebtools extract mydisc.dsd T.LOTTERY -o lottery.bas -t utf8

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ For a quick overview and installation instructions, see the
2323

2424
## Guides
2525

26+
- [The .inf sidecar format](https://github.com/acscpt/beebtools/blob/main/docs/inf-format.md) - syntax, keys, extraction layouts, CRC validation
2627
- [Pretty-printer](https://github.com/acscpt/beebtools/blob/main/docs/pretty-printer.md) - operator spacing, anti-listing traps
2728
- [Library usage](https://github.com/acscpt/beebtools/blob/main/docs/library.md) - using beebtools as a Python library

docs/commands/build.md

Lines changed: 60 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# build - Build a disc image from files
22

33
```bash
4-
beebtools build <dir> <output> [-t 40|80] [--title TITLE] [--boot OFF|LOAD|RUN|EXEC] [--strict]
4+
beebtools build <dir> <output> [-t 40|80] [--title TITLE] [--boot OFF|LOAD|RUN|EXEC] [--force] [--strict]
55
```
66

77
Assembles a disc image from a directory of files with `.inf` sidecars. The
@@ -10,70 +10,81 @@ or `.adl`).
1010

1111
## Source directory layout
1212

13-
The source directory should have the same hierarchical layout produced by
14-
`extract -a --inf`:
13+
The source directory should have the layout produced by `extract -a`. The
14+
builder supports both flat (default) and hierarchical layouts.
1515

16-
- **DFS**: one subdirectory per directory character (`$/`, `T/`), with each
17-
data file accompanied by a `.inf` sidecar. For DSD images, `side0/` and
18-
`side1/` subdirectories are expected.
16+
### Flat layout (default from `extract -a`)
1917

20-
- **ADFS**: a `$` directory at the top level containing the file hierarchy,
21-
with subdirectories matching the ADFS tree structure. Subdirectories are
22-
created on the image automatically.
18+
All files sit in one directory, named by their full Acorn path with dots
19+
as separators. Each data file has a companion `.inf` sidecar. A `$.inf`
20+
carries the disc title and boot option.
2321

24-
### DFS example layout
22+
**DFS example:**
2523

2624
```
2725
working/
28-
$/
29-
BOOT
30-
BOOT.inf # $.BOOT FF1900 FF8023 000100
31-
MENU
32-
MENU.inf # $.MENU FF0E00 FF802B 000400
33-
T/
34-
MYPROG
35-
MYPROG.inf # T.MYPROG FF0E00 FF802B 000800
26+
$.inf # $ 00000000 00000000 00000000 00 TITLE=MY%20DISC OPT=3
27+
$.BOOT.bin
28+
$.BOOT.bin.inf # $.BOOT FF1900 FF8023 000100 00
29+
$.MENU.bas
30+
$.MENU.bas.inf # $.MENU FF0E00 FF802B 000400 00
31+
T.MYPROG.bas
32+
T.MYPROG.bas.inf # T.MYPROG FF0E00 FF802B 000800 00
33+
```
34+
35+
**ADFS example:**
36+
37+
```
38+
working/
39+
$.inf
40+
$.GAMES.inf # directory metadata
41+
$.GAMES.ELITE.bin
42+
$.GAMES.ELITE.bin.inf # $.GAMES.ELITE FFFF0E00 FFFF802B 004000 00
43+
$.DATA.inf # directory metadata
44+
$.DATA.SCORES.bin
45+
$.DATA.SCORES.bin.inf # $.DATA.SCORES FF0000 FF0000 001000 00
3646
```
3747

38-
Each `.inf` file uses the standard DFS format: `DIR.NAME LOAD EXEC SIZE [L]`.
39-
The directory character in the `.inf` content matches the subdirectory the file
40-
sits in.
48+
For DSD images, the builder expects `side0/` and `side1/` subdirectories,
49+
each with its own flat layout and `$.inf`.
4150

42-
### ADFS example layout
51+
### Hierarchical layout (from `extract -a --mkdirs`)
52+
53+
One subdirectory per DFS directory character or ADFS path. This is the
54+
layout produced when `--mkdirs` is passed to `extract`.
55+
56+
**DFS example:**
4357

4458
```
4559
working/
60+
$.inf
4661
$/
47-
BOOT
48-
BOOT.inf # $.BOOT FF1900 FF8023 000100
49-
GAMES/
50-
ELITE
51-
ELITE.inf # $.GAMES.ELITE FFFF0E00 FFFF802B 004000
52-
DATA/
53-
SCORES
54-
SCORES.inf # $.GAMES.DATA.SCORES FF0000 FF0000 001000
62+
BOOT.bin
63+
BOOT.bin.inf # $.BOOT FF1900 FF8023 000100 00
64+
T/
65+
MYPROG.bas
66+
MYPROG.bas.inf # T.MYPROG FF0E00 FF802B 000800 00
5567
```
5668

57-
For ADFS, the directory tree on the filesystem mirrors the ADFS directory
58-
hierarchy. Each `.inf` sidecar must contain the **full ADFS path** of the file
59-
(e.g. `$.GAMES.ELITE`, not just `$.ELITE`), because `build` reads the path
60-
from the `.inf` content when adding the file to the image.
69+
The builder auto-detects which layout is present by looking for `.inf`
70+
sidecars in the source tree. Both layouts round-trip correctly.
6171

62-
Subdirectories do not need their own `.inf` files - `build` creates them
63-
automatically as it walks the filesystem tree.
72+
### $.inf as source of truth
6473

65-
## Round-trip workflow
74+
When a `$.inf` exists in the source directory, the builder uses its
75+
`TITLE=` and `OPT=` values for the disc title and boot option. Explicit
76+
`--title`/`--boot` flags emit a warning when they conflict with `$.inf`;
77+
pass `--force` to override.
6678

67-
The easiest way to get a valid source directory is to extract from an existing
68-
image:
79+
## Round-trip workflow
6980

7081
```bash
7182
# DFS round-trip
72-
beebtools extract original.ssd -a --inf -d working/
83+
beebtools extract original.ssd -a -d working/
7384
beebtools build working/ modified.ssd --title "MODIFIED"
7485

7586
# ADFS round-trip
76-
beebtools extract original.adf -a --inf -d working/
87+
beebtools extract original.adf -a -d working/
7788
beebtools build working/ modified.adf --title "MODIFIED"
7889
```
7990

@@ -95,14 +106,12 @@ beebtools add mydisc.adf game.bin --name $.GAMES.ELITE --load 0E00 --exec 802B
95106

96107
**Option 2: `build`** (better for many files)
97108

98-
Create the directory tree manually with `.inf` sidecars, then build in one
99-
step. You must write the `.inf` files yourself with the correct paths and
100-
addresses:
109+
Create a directory with `.inf` sidecars, then build in one step. You must
110+
write the `.inf` files yourself with the correct paths and addresses:
101111

102112
```bash
103-
mkdir -p working/\$/GAMES
104-
echo '$.LOADER 001900 001900 000400' > working/\$/LOADER.inf
105-
echo '$.GAMES.ELITE FFFF0E00 FFFF802B 004000' > working/\$/GAMES/ELITE.inf
113+
echo '$.LOADER 001900 001900 000400 00' > working/$.LOADER.bin.inf
114+
echo '$.GAMES.ELITE FFFF0E00 FFFF802B 004000 00' > working/$.GAMES.ELITE.bin.inf
106115
# ... copy the actual data files alongside each .inf ...
107116
beebtools build working/ mydisc.adf --title "MY DISC"
108117
```
@@ -114,7 +123,7 @@ the builder places the file at that exact sector instead of allocating a fresh
114123
range. This enables byte-exact round-trips on discs whose catalogue entries
115124
share sectors (notably Level 9 copy-protected games).
116125

117-
`extract -a --inf` writes `X_START_SECTOR` on every sidecar automatically, so
126+
`extract -a` writes `X_START_SECTOR` on every sidecar automatically, so
118127
the default extract-and-rebuild cycle preserves original sector positions without
119128
any extra flags.
120129

@@ -131,6 +140,9 @@ for the full story.
131140

132141
- `--boot` - boot option: OFF, LOAD, RUN, or EXEC (numbers 0-3 also accepted)
133142

143+
- `--force` - override `$.inf` disc metadata with explicit `--title`/`--boot`
144+
values
145+
134146
- `--strict` - enforce DFS spec-compliance on filenames. Rejects non-printable
135147
bytes, `.`, `#`, `*`, `:`, `"`, and space. Use when authoring new discs where
136148
spec conformance matters. Default behaviour accepts any 7-bit byte, matching

0 commit comments

Comments
 (0)