oaknut-zip 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Robert Smallshire
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,526 @@
1
+ Metadata-Version: 2.4
2
+ Name: oaknut-zip
3
+ Version: 0.1.0
4
+ Summary: Work with ZIP files containing Acorn computer metadata.
5
+ Author-email: Robert Smallshire <robert@smallshire.org.uk>
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: click>=8.0
19
+ Requires-Dist: rich>=13.0
20
+ Requires-Dist: xattr>=1.0; sys_platform == "darwin"
21
+ Dynamic: license-file
22
+
23
+ # oaknut-zip
24
+
25
+ A standalone Python script for extracting ZIP files containing
26
+ [Acorn computer](https://en.wikipedia.org/wiki/Acorn_Computers) metadata.
27
+
28
+ Standard unzip tools silently discard the load addresses, execution addresses,
29
+ and file attributes that Acorn systems (BBC Micro, Master, Archimedes, RISC OS
30
+ machines) store in ZIP archives. oaknut-zip preserves this metadata, writing it out
31
+ in your choice of four formats.
32
+
33
+ ## The problem
34
+
35
+ When software for Acorn 8-bit and 32-bit systems is distributed as ZIP files,
36
+ the archives often contain platform-specific metadata embedded using mechanisms
37
+ that non-Acorn unzip tools ignore:
38
+
39
+ - **SparkFS extra fields** in the ZIP structure itself, carrying load/exec
40
+ addresses and RISC OS file attributes.
41
+ - **Bundled INF sidecar files** inside the archive, in either Acorn
42
+ or PiEconetBridge format.
43
+ - **Unix filename encoding**, where metadata is appended to filenames as comma-
44
+ separated hex suffixes.
45
+
46
+ Extracting these archives with `unzip` or Python's `zipfile` module on Linux or
47
+ macOS produces the raw file data but loses all the Acorn metadata. For 6502 or
48
+ ARM executables, this means you no longer know where in memory a program should
49
+ be loaded or where execution should begin. For a PiEconetBridge fileserver, the
50
+ files become unserviceable without their attributes.
51
+
52
+ oaknut-zip reads all three metadata sources from the ZIP and writes them out in a
53
+ format your target system can consume.
54
+
55
+ ## Prerequisites
56
+
57
+ oaknut-zip requires only [`uv`](https://docs.astral.sh/uv/). `uv` handles
58
+ Python installation, dependency resolution, and virtual environments
59
+ automatically --- you do not need to install anything else by hand.
60
+
61
+ ### Installing uv
62
+
63
+ **macOS (Homebrew):**
64
+
65
+ ```
66
+ brew install uv
67
+ ```
68
+
69
+ **Linux / macOS (standalone installer):**
70
+
71
+ ```
72
+ curl -LsSf https://astral.sh/uv/install.sh | sh
73
+ ```
74
+
75
+ **Windows:**
76
+
77
+ ```
78
+ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
79
+ ```
80
+
81
+ See the [uv installation docs](https://docs.astral.sh/uv/getting-started/installation/)
82
+ for other methods including pip, pipx, Cargo, Conda, Winget, and Scoop.
83
+
84
+ ## Usage
85
+
86
+ oaknut-zip is a single-file [PEP 723](https://peps.python.org/pep-0723/) script
87
+ with an embedded dependency table. Run it directly:
88
+
89
+ ```
90
+ ./oaknut_zip.py <command> [options]
91
+ ```
92
+
93
+ Or explicitly via uv:
94
+
95
+ ```
96
+ uv run oaknut_zip.py <command> [options]
97
+ ```
98
+
99
+ ### Commands
100
+
101
+ ```
102
+ $ ./oaknut_zip.py --help
103
+ Usage: oaknut_zip.py [OPTIONS] COMMAND [ARGS]...
104
+
105
+ Work with ZIP files containing Acorn computer metadata.
106
+
107
+ Options:
108
+ --version Show the version and exit.
109
+ --help Show this message and exit.
110
+
111
+ Commands:
112
+ extract Extract a ZIP file, preserving Acorn metadata.
113
+ info Show summary of Acorn metadata in a ZIP file.
114
+ list List ZIP contents showing Acorn metadata.
115
+ ```
116
+
117
+ ## Inspecting an archive
118
+
119
+ ### list --- tabular view of all entries
120
+
121
+ ```
122
+ $ ./oaknut_zip.py list NetUtils.zip
123
+ NetUtils.zip
124
+ ┏━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━┳━━━━━━━━━┓
125
+ ┃ Filename ┃ Load ┃ Exec ┃ Length ┃ Attr ┃ Type ┃ Source ┃
126
+ ┡━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━╇━━━━━━━━━┩
127
+ │ Free │ FFFF0E10 │ FFFF0E10 │ 000001E6 │ C18325D │ F0E │ sparkfs │
128
+ │ FSList │ FFFF0900 │ FFFF0900 │ 00000200 │ 1415365D │ F09 │ sparkfs │
129
+ │ PSList │ FFFF0900 │ FFFF0900 │ 000001B5 │ B861C5D │ F09 │ sparkfs │
130
+ │ Notify │ FFFF0E23 │ FFFF0E23 │ 0000012A │ C6B1E5D │ F0E │ sparkfs │
131
+ │ Remote │ FFFF0E10 │ FFFF0E10 │ 000001ED │ 1347085D │ F0E │ sparkfs │
132
+ │ Servers │ FFFF0900 │ FFFF091A │ 000001CF │ 17162C5D │ F09 │ sparkfs │
133
+ │ SetStation │ FFFFDD00 │ FFFFDD00 │ 00000200 │ 1761575D │ FDD │ sparkfs │
134
+ │ Stations │ FFFF08D5 │ FFFF08E1 │ 0000022B │ 13615A57 │ F08 │ sparkfs │
135
+ │ SJMon │ FFFF1B00 │ FFFF1B00 │ 00000D7E │ F6040D │ F1B │ sparkfs │
136
+ │ Users │ FFFF0E23 │ FFFF0E23 │ 00000139 │ 15355D │ F0E │ sparkfs │
137
+ │ View │ FFFF0900 │ FFFF0904 │ 000001FF │ 135A115D │ F09 │ sparkfs │
138
+ │ ReadMe │ FFFFFF52 │ 2FEEAFD0 │ 00000255 │ BEB2A55 │ FFF │ sparkfs │
139
+ └────────────┴──────────┴──────────┴──────────┴──────────┴──────┴─────────┘
140
+ ```
141
+
142
+ The **Source** column shows where the metadata was found (see the
143
+ [format identifier table](#extracting-an-archive) below). The **Type** column
144
+ shows the RISC OS filetype extracted from the load address (when the top
145
+ 12 bits are `0xFFF`).
146
+
147
+ ### info --- summary statistics
148
+
149
+ ```
150
+ $ ./oaknut_zip.py info NetUtils.zip
151
+ Archive: NetUtils.zip
152
+ Files: 12
153
+ Dirs: 0
154
+ SparkFS: 12 files with ARC0 extra fields
155
+ inf-trad: 0 files with bundled traditional INF
156
+ inf-pieb: 0 files with bundled PiEconetBridge INF
157
+ Filename: 0 files with encoded filenames
158
+ Plain: 0 files without Acorn metadata
159
+ Filetypes: 6 distinct
160
+ F08: 1 files
161
+ F09: 4 files
162
+ F0E: 4 files
163
+ F1B: 1 files
164
+ FDD: 1 files
165
+ FFF: 1 files
166
+ ```
167
+
168
+ ## Extracting an archive
169
+
170
+ ```
171
+ $ ./oaknut_zip.py extract --help
172
+ Usage: oaknut_zip.py extract [OPTIONS] ZIPFILE_PATH
173
+
174
+ Extract a ZIP file, preserving Acorn metadata.
175
+
176
+ Options:
177
+ -d, --output-dir PATH Output directory (default: ZIP filename
178
+ without extension).
179
+ -v, --verbose Show extraction progress.
180
+ --meta-format [inf-trad|inf-pieb|xattr|filename-riscos|filename-mos|none]
181
+ Metadata format: inf-trad, inf-pieb, xattr,
182
+ filename-riscos, filename-mos, or none.
183
+ --no-decode-filenames Do not decode metadata from filename
184
+ suffixes (,xxx or ,load,exec).
185
+ --owner INTEGER Econet owner ID for inf-pieb files (default:
186
+ 0 = SYST).
187
+ --help Show this message and exit.
188
+ ```
189
+
190
+ oaknut-zip uses a consistent `<mechanism>-<flavour>` naming scheme for format
191
+ identifiers. The same identifiers appear as metadata source labels in
192
+ the `list` and `info` commands, and as `--meta-format` choices for output:
193
+
194
+ | Identifier | As input source | As output format (`--meta-format`) |
195
+ |-------------------|-----------------|------------------------------------|
196
+ | `sparkfs` | SparkFS/ARC0 extra fields in the ZIP structure | --- (not an output format) |
197
+ | `inf-trad` | Bundled traditional `.inf` sidecar files | Traditional INF sidecar files |
198
+ | `inf-pieb` | Bundled PiEconetBridge `.inf` sidecar files | PiEconetBridge INF sidecar files |
199
+ | `filename` | Metadata encoded in filenames (`,xxx`, `,load,exec`, `,load-exec`) | --- (not an output format) |
200
+ | `filename-riscos` | --- | RISC OS filename encoding (`,xxx` or `,llllllll,eeeeeeee`) |
201
+ | `filename-mos` | --- | MOS filename encoding (`,load-exec`) |
202
+ | `xattr` | --- | Extended attributes (`user.econet_*`) |
203
+ | `none` | --- | No metadata (raw extraction) |
204
+
205
+ ### Format 1: Traditional INF (default)
206
+
207
+ ```
208
+ ./oaknut_zip.py extract NetUtils.zip
209
+ ```
210
+
211
+ Each extracted file gets a companion `.inf` sidecar file:
212
+
213
+ ```
214
+ $ cat SetStation.inf
215
+ SetStation FFFFDD00 FFFFDD00 00000200 1761575D
216
+
217
+ $ cat ReadMe.inf
218
+ ReadMe FFFFFF52 2FEEAFD0 00000255 BEB2A55
219
+ ```
220
+
221
+ The fields are space-separated, with the filename left-padded to 11 characters:
222
+
223
+ ```
224
+ filename load exec length [access]
225
+ ```
226
+
227
+ | Field | Format | Description |
228
+ |----------|-----------------|-----------------------------------------------------------------------------|
229
+ | filename | ASCII string | Leaf filename, left-padded to 11 chars |
230
+ | load | 8-digit hex | 32-bit load address; if top 12 bits = `FFF`, bits 8--19 encode the filetype |
231
+ | exec | 8-digit hex | 32-bit execution address (entry point for code) |
232
+ | length | 8-digit hex | File length in bytes |
233
+ | access | 2-digit hex | Acorn file attributes (optional; see attribute bits below) |
234
+
235
+ This is the standard format understood by BBC Micro emulators, disc image tools
236
+ (BBCIM, Disc Image Manager), and other retro-computing utilities.
237
+
238
+ ### Format 2: PiEconetBridge INF
239
+
240
+ ```
241
+ ./oaknut_zip.py extract --meta-format inf-pieb NetUtils.zip
242
+ ```
243
+
244
+ Writes `.inf` sidecar files in the format used by
245
+ [PiEconetBridge](https://github.com/cr12925/PiEconetBridge):
246
+
247
+ ```
248
+ $ cat SetStation.inf
249
+ 0 ffffdd00 ffffdd00 1761575d
250
+
251
+ $ cat ReadMe.inf
252
+ 0 ffffff52 2feeafd0 beb2a55
253
+ ```
254
+
255
+ The fields, all lowercase hex:
256
+
257
+ ```
258
+ owner load exec perm
259
+ ```
260
+
261
+ | Field | Format | Description |
262
+ |-------|--------------|--------------------------------------------------------|
263
+ | owner | short hex | Econet user ID (set with `--owner`, default 0 = SYST) |
264
+ | load | long hex | 32-bit load address |
265
+ | exec | long hex | 32-bit execution address |
266
+ | perm | short hex | File permissions byte |
267
+
268
+ PiEconetBridge reads this format from `<filename>.inf` when extended attribute
269
+ support is unavailable on the host filesystem (e.g. FAT32). This is the format
270
+ produced by the `parse_acorn_zip.pl` utility bundled with PiEconetBridge.
271
+
272
+ ### Format 3: Extended attributes (xattr)
273
+
274
+ ```
275
+ ./oaknut_zip.py extract --meta-format xattr NetUtils.zip
276
+ ```
277
+
278
+ Writes metadata directly into the filesystem's extended attributes, with no
279
+ sidecar files:
280
+
281
+ ```
282
+ $ xattr -l SetStation
283
+ user.econet_exec: FFFFDD00
284
+ user.econet_load: FFFFDD00
285
+ user.econet_owner: 0000
286
+ user.econet_perm: 1761575D
287
+
288
+ $ xattr -l ReadMe
289
+ user.econet_exec: 2FEEAFD0
290
+ user.econet_load: FFFFFF52
291
+ user.econet_owner: 0000
292
+ user.econet_perm: BEB2A55
293
+ ```
294
+
295
+ The attribute names and value formats match PiEconetBridge's C implementation
296
+ exactly:
297
+
298
+ | Attribute | Format (sprintf) | Example |
299
+ |----------------------|------------------|--------------|
300
+ | `user.econet_owner` | `%04X` | `0000` |
301
+ | `user.econet_load` | `%08X` | `FFFFDD00` |
302
+ | `user.econet_exec` | `%08X` | `FFFFDD00` |
303
+ | `user.econet_perm` | `%02X` | `03` |
304
+
305
+ This is the preferred format for PiEconetBridge when the host filesystem
306
+ supports extended attributes (ext4, XFS, HFS+, APFS). PiEconetBridge reads
307
+ xattrs in preference to `.inf` files when both are present.
308
+
309
+ ### Format 4: RISC OS filename encoding
310
+
311
+ ```
312
+ ./oaknut_zip.py extract --meta-format filename-riscos NetUtils.zip
313
+ ```
314
+
315
+ Encodes metadata directly into the output filenames using comma-separated hex
316
+ suffixes. No sidecar files are created. Filetype-stamped files get a `,xxx`
317
+ suffix (e.g. `FILE,fdd`); files with literal load/exec addresses get
318
+ `,llllllll,eeeeeeee` (e.g. `PROG,00001900,0000801f`).
319
+
320
+ This is the most portable format --- it requires no filesystem-specific
321
+ support and survives transfers between any systems. Files already carrying
322
+ the correct suffix are not double-encoded.
323
+
324
+ ### Format 5: MOS filename encoding
325
+
326
+ ```
327
+ ./oaknut_zip.py extract --meta-format filename-mos NetUtils.zip
328
+ ```
329
+
330
+ Encodes metadata into output filenames using the `,load-exec` convention
331
+ documented in the [BeebWiki INF file format](https://beebwiki.mdfs.net/INF_file_format)
332
+ specification. This format uses a comma prefix with load and exec addresses
333
+ separated by a dash, with variable-width hex digits (no zero-padding):
334
+ `,1900-801f`.
335
+
336
+ This is the convention used by MOS and SparkFS when encoding addresses in
337
+ filenames. Unlike the RISC OS form (Format 4), it always encodes full
338
+ load/exec addresses and does not special-case filetype-stamped files.
339
+
340
+ ### Format 6: No metadata
341
+
342
+ ```
343
+ ./oaknut_zip.py extract --meta-format none NetUtils.zip
344
+ ```
345
+
346
+ Extracts files only, discarding all Acorn metadata. Equivalent to a standard
347
+ `unzip`.
348
+
349
+ ## Metadata sources in ZIP files
350
+
351
+ oaknut-zip reads Acorn metadata from three sources within ZIP archives. When
352
+ multiple sources are present for a given entry, they are used in priority order:
353
+ SparkFS extra fields, then bundled INF sidecar files, then filename encoding.
354
+
355
+ ### SparkFS extra fields (ARC0)
356
+
357
+ ZIP archives created by [SparkFS](http://www.davidpilling.com/wiki/index.php/SparkFS),
358
+ SparkPlug, or RISC OS zip tools embed Acorn metadata as a ZIP extra field with
359
+ header ID `0x4341` ("AC") and an "ARC0" signature.
360
+
361
+ The extra field layout (all values little-endian), as defined in the
362
+ [Info-ZIP extra field specification](https://libzip.org/specifications/extrafld.txt)
363
+ by [David Pilling](http://www.davidpilling.com/wiki/index.php/SparkFS):
364
+
365
+ | Offset | Size | Description |
366
+ |--------|---------|--------------------------------|
367
+ | 0 | 4 bytes | Signature `"ARC0"` |
368
+ | 4 | 4 bytes | Load address (uint32) |
369
+ | 8 | 4 bytes | Execution address (uint32) |
370
+ | 12 | 4 bytes | File attributes (uint32) |
371
+ | 16 | 4 bytes | Reserved (zero) |
372
+
373
+ This is the most reliable metadata source, as it is embedded in the ZIP
374
+ structure itself and survives transfers between any systems.
375
+
376
+ ### Bundled INF sidecar files
377
+
378
+ Some ZIP archives include `.inf` sidecar files alongside the data files they
379
+ describe. oaknut-zip detects and parses these automatically, supporting both
380
+ flavours:
381
+
382
+ - **Traditional INF** (`filename load exec length [access]`) --- reported
383
+ as source `inf-trad` in the list and info commands.
384
+ - **PiEconetBridge INF** (`owner load exec perm`) --- reported as source
385
+ `inf-pieb`.
386
+
387
+ When extracting, bundled `.inf` files are consumed as a metadata source rather
388
+ than extracted as separate files. The metadata is then written in whatever
389
+ output format was requested (inf-trad, inf-pieb, xattr, etc.). This allows,
390
+ for example, converting a PiEconetBridge archive to xattr format in a single
391
+ step.
392
+
393
+ With `--meta-format none`, bundled `.inf` files are extracted as-is (no
394
+ metadata is consumed).
395
+
396
+ ### Unix filename encoding
397
+
398
+ Because Unix filesystems have no way to store Acorn load/exec addresses
399
+ natively, a [convention](https://www.riscos.info/index.php/RISC_OS_Filename_Translation)
400
+ exists for encoding the RISC OS filetype into the filename itself as a
401
+ comma-separated hex suffix:
402
+
403
+ | Pattern | Example | Meaning |
404
+ |---------------------------------|----------------------------|-----------------------------|
405
+ | `filename,xxx` | `HELLO,ffb` | RISC OS filetype `FFB` |
406
+
407
+ oaknut-zip also supports two additional forms that encode full load and exec
408
+ addresses for files that are not filetype-stamped:
409
+
410
+ | Pattern | Example | Meaning |
411
+ |---------------------------------|----------------------------|-----------------------------|
412
+ | `filename,llllllll,eeeeeeee` | `PROG,ffff0e10,0000801f` | Load and exec addresses |
413
+ | `filename,load-exec` | `PROG,1900-801f` | Load and exec (MOS/SparkFS) |
414
+
415
+ The first form is used by
416
+ [python-zipinfo-riscos](https://github.com/gerph/python-zipinfo-riscos)
417
+ and requires exactly 8 hex digits per address. The second form, documented
418
+ in the [BeebWiki INF file format](https://beebwiki.mdfs.net/INF_file_format)
419
+ specification, uses variable-width hex digits separated by a dash and is the
420
+ convention used by MOS and SparkFS.
421
+
422
+ oaknut-zip strips these suffixes from output filenames during extraction.
423
+ Use `--no-decode-filenames` to preserve the encoded filenames as-is.
424
+
425
+ ## RISC OS filetype encoding
426
+
427
+ When the top 12 bits of a load address are `0xFFF`, the address does not
428
+ represent a literal memory location. Instead, bits 8--19 encode a
429
+ [RISC OS filetype](https://en.wikipedia.org/wiki/List_of_RISC_OS_filetypes):
430
+
431
+ ```
432
+ Load address: FFFtttxx
433
+ ^^^
434
+ filetype (3 hex digits)
435
+ ```
436
+
437
+ Example RISC OS filetypes:
438
+
439
+ | Type | Name | Description |
440
+ |-------|------------|-------------------------------------------|
441
+ | `FF9` | Sprite | Sprite or saved screen |
442
+ | `FFA` | Module | Relocatable module |
443
+ | `FFB` | BASIC | Tokenised BASIC program |
444
+ | `FFC` | Utility | Position independent code |
445
+ | `FFD` | Data | Arbitrary data |
446
+ | `FFE` | Command | Command (Exec) file |
447
+ | `FFF` | Text | Plain ASCII text with LF newlines |
448
+
449
+ ## Acorn file attribute bits
450
+
451
+ The attribute byte encodes access permissions in the Acorn filing system
452
+ convention:
453
+
454
+ | Bit | Mask | Meaning |
455
+ |-----|--------|------------------|
456
+ | 0 | `0x01` | Owner writable |
457
+ | 1 | `0x02` | Owner readable |
458
+ | 3 | `0x08` | Locked (DFS `L`) |
459
+ | 4 | `0x10` | Public writable |
460
+ | 5 | `0x20` | Public readable |
461
+
462
+ A file with attributes `0x03` is owner-readable and owner-writable (WR).
463
+ A value of `0x17` adds public read access (WR/R), which is the default
464
+ for files served by PiEconetBridge.
465
+
466
+ ## Running the tests
467
+
468
+ ```
469
+ uv run --with pytest --with xattr pytest tests/ -v
470
+ ```
471
+
472
+ The test suite covers all parsing, formatting, and extraction logic,
473
+ including integration tests against six real-world ZIP fixtures:
474
+
475
+ - **NetUtils.zip** --- Econet utilities from [MDFS](https://mdfs.net/Apps/Networking/),
476
+ with SparkFS/ARC0 extra fields. This is the archive that motivated the project.
477
+ - **NetUtilB.zip** --- A second Econet utilities pack, also with SparkFS metadata.
478
+ - **MASTER.zip** --- BBC Master utilities from
479
+ [MDFS](https://mdfs.net/Mirror/Archive/SJ/MDFS/MASTER.zip), 344 files with
480
+ SparkFS metadata and 36 distinct filetypes.
481
+ - **sweh_econet_system.zip** --- PiEconetBridge system files from
482
+ [sweh](https://sweh.spuddy.org/tmp/econet-bridge/), containing bundled
483
+ PiEconetBridge-format `.inf` sidecar files (no SparkFS extra fields).
484
+ - **testdir-unix.zip** --- Test archive from
485
+ [python-zipinfo-riscos](https://github.com/gerph/python-zipinfo-riscos),
486
+ with Unix filename encoding (`,xxx` suffixes).
487
+ - **testdir-ro.zip** --- The same content as testdir-unix.zip but with SparkFS
488
+ extra fields instead of filename encoding.
489
+
490
+ ## References
491
+
492
+ ### Metadata format specifications
493
+
494
+ - [INF file format](https://beebwiki.mdfs.net/INF_file_format) ---
495
+ BeebWiki specification for the Acorn `.inf` sidecar format.
496
+ - [ZIP extra field registry](https://libzip.org/specifications/extrafld.txt) ---
497
+ Known ZIP extra field types, including the Acorn/SparkFS entry (`0x4341`).
498
+ - [RISC OS filetype](http://justsolve.archiveteam.org/wiki/RISC_OS_filetype) ---
499
+ How RISC OS encodes filetypes in load addresses.
500
+ - [ZipToInf documentation](https://mdfs.net/Apps/Archivers/ZipTools/ZipToInf.txt) ---
501
+ J.G.Harston's ZipToInf utility and INF format description.
502
+ - [RISC OS filename translation](https://www.riscos.info/index.php/RISC_OS_Filename_Translation) ---
503
+ Documents the `,xxx` filetype suffix convention used on Unix filesystems.
504
+ - [INF file format](https://mdfs.net/Docs/Comp/BBC/FileFormat/INFfile) ---
505
+ J.G.Harston's detailed INF file format specification.
506
+
507
+ ### Tools and related projects
508
+
509
+ - [PiEconetBridge](https://github.com/cr12925/PiEconetBridge) ---
510
+ Econet bridge for Raspberry Pi; defines the `user.econet_*` xattr convention
511
+ and the alternative `.inf` format (`owner load exec perm`).
512
+ - [python-zipinfo-riscos](https://github.com/gerph/python-zipinfo-riscos) ---
513
+ Python library for reading/writing RISC OS extra fields in ZIP archives;
514
+ source of the `,llllllll,eeeeeeee` load/exec filename encoding convention.
515
+ - [ZipTools](https://mdfs.net/Apps/Archivers/ZipTools/) ---
516
+ J.G.Harston's suite of Acorn-aware ZIP utilities including ZipToInf.
517
+ - [SparkFS](http://www.davidpilling.com/wiki/index.php/SparkFS) ---
518
+ David Pilling's RISC OS filing system for ZIP/Arc/Spark archives;
519
+ originator of the ARC0 extra field format.
520
+
521
+ ### Forum discussion
522
+
523
+ - [Stardot forum thread](https://stardot.org.uk/forums/viewtopic.php?p=478751#p478751) ---
524
+ Discussion of the metadata-loss problem that motivated this tool.
525
+ - [INF format discussion](https://www.stardot.org.uk/forums/viewtopic.php?t=31577) ---
526
+ Community discussion on INF format variants and standardisation.