oaknut-romfs 12.3.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,67 @@
1
+ Metadata-Version: 2.4
2
+ Name: oaknut-romfs
3
+ Version: 12.3.0
4
+ Summary: Python library for Acorn ROM Filing System (ROMFS) paged-ROM images
5
+ Author-email: Robert Smallshire <robert@smallshire.org.uk>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/rob-smallshire/oaknut/tree/master/packages/oaknut-romfs
8
+ Project-URL: Documentation, https://rob-smallshire.github.io/oaknut/disc/api/reference/romfs.html
9
+ Project-URL: Repository, https://github.com/rob-smallshire/oaknut
10
+ Project-URL: Issues, https://github.com/rob-smallshire/oaknut/issues
11
+ Classifier: Development Status :: 2 - Pre-Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: oaknut-file>=10.0
24
+ Requires-Dist: oaknut-discimage>=10.0
25
+ Requires-Dist: oaknut-filesystem>=10.0
26
+ Requires-Dist: oaknut-basic>=10.0
27
+ Provides-Extra: cli
28
+ Requires-Dist: oaknut-cli>=10.0; extra == "cli"
29
+ Dynamic: license-file
30
+
31
+ # oaknut-romfs
32
+
33
+ Acorn ROM Filing System (ROMFS) support for the [oaknut](https://github.com/rob-smallshire/oaknut)
34
+ family of packages.
35
+
36
+ ROMFS is the filing system for paged ROMs on the BBC Micro and Acorn
37
+ Electron — sideways ROMs and cartridges. It stores files in the same
38
+ block layout as the Cassette Filing System (CFS), with the ROM image
39
+ standing in for the tape: each file is a chain of CFS-format blocks
40
+ carrying Acorn load and execution addresses, a block number, a length,
41
+ a flag byte, and header and data CRCs, introduced by a standard
42
+ paged-ROM service header.
43
+
44
+ The medium is read-only ROM, so the filing system is flat: there are no
45
+ directories, and a file's metadata is the load/exec pair plus a lock
46
+ bit, exactly as on cassette.
47
+
48
+ This package contributes ROMFS to the `oaknut.filesystem` extension axis,
49
+ so ROMFS images are identified, listed and read through the `disc` CLI
50
+ alongside the disc-based filing systems.
51
+
52
+ ## Status
53
+
54
+ Pre-alpha. The package is being built up format-first: see
55
+ [`docs/romfs-format-spec.md`](docs/romfs-format-spec.md) for the on-ROM
56
+ byte layout and [`docs/architecture.md`](docs/architecture.md) for the
57
+ package design and its mapping onto the `oaknut.filesystem` contract.
58
+
59
+ ## Installation
60
+
61
+ ```sh
62
+ uv add oaknut-romfs
63
+ ```
64
+
65
+ ## Licence
66
+
67
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,37 @@
1
+ # oaknut-romfs
2
+
3
+ Acorn ROM Filing System (ROMFS) support for the [oaknut](https://github.com/rob-smallshire/oaknut)
4
+ family of packages.
5
+
6
+ ROMFS is the filing system for paged ROMs on the BBC Micro and Acorn
7
+ Electron — sideways ROMs and cartridges. It stores files in the same
8
+ block layout as the Cassette Filing System (CFS), with the ROM image
9
+ standing in for the tape: each file is a chain of CFS-format blocks
10
+ carrying Acorn load and execution addresses, a block number, a length,
11
+ a flag byte, and header and data CRCs, introduced by a standard
12
+ paged-ROM service header.
13
+
14
+ The medium is read-only ROM, so the filing system is flat: there are no
15
+ directories, and a file's metadata is the load/exec pair plus a lock
16
+ bit, exactly as on cassette.
17
+
18
+ This package contributes ROMFS to the `oaknut.filesystem` extension axis,
19
+ so ROMFS images are identified, listed and read through the `disc` CLI
20
+ alongside the disc-based filing systems.
21
+
22
+ ## Status
23
+
24
+ Pre-alpha. The package is being built up format-first: see
25
+ [`docs/romfs-format-spec.md`](docs/romfs-format-spec.md) for the on-ROM
26
+ byte layout and [`docs/architecture.md`](docs/architecture.md) for the
27
+ package design and its mapping onto the `oaknut.filesystem` contract.
28
+
29
+ ## Installation
30
+
31
+ ```sh
32
+ uv add oaknut-romfs
33
+ ```
34
+
35
+ ## Licence
36
+
37
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,59 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "oaknut-romfs"
7
+ dynamic = ["version"]
8
+ authors = [{ name = "Robert Smallshire", email = "robert@smallshire.org.uk" }]
9
+ description = "Python library for Acorn ROM Filing System (ROMFS) paged-ROM images"
10
+ readme = "README.md"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ requires-python = ">=3.11"
14
+ classifiers = [
15
+ "Development Status :: 2 - Pre-Alpha",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3 :: Only",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ ]
25
+ dependencies = [
26
+ "oaknut-file>=10.0",
27
+ "oaknut-discimage>=10.0",
28
+ "oaknut-filesystem>=10.0",
29
+ "oaknut-basic>=10.0",
30
+ ]
31
+
32
+ # The `cli` extra pulls the shared CLI toolkit so this package can
33
+ # contribute `disc romfs` subcommands. The library core never imports it,
34
+ # so a bare install stays CLI-free.
35
+ [project.optional-dependencies]
36
+ cli = ["oaknut-cli>=10.0"]
37
+
38
+ [project.urls]
39
+ Homepage = "https://github.com/rob-smallshire/oaknut/tree/master/packages/oaknut-romfs"
40
+ Documentation = "https://rob-smallshire.github.io/oaknut/disc/api/reference/romfs.html"
41
+ Repository = "https://github.com/rob-smallshire/oaknut"
42
+ Issues = "https://github.com/rob-smallshire/oaknut/issues"
43
+
44
+ # Filesystem contributed to the oaknut.filesystem axis — discovered at
45
+ # runtime via this entry point, so the detector ships with the format it
46
+ # detects.
47
+ [project.entry-points."oaknut.filesystem"]
48
+ acorn-romfs = "oaknut.romfs.filesystem:AcornROMFS"
49
+
50
+ # Admin subcommands contributed to the `disc` CLI on the oaknut.command axis:
51
+ # `disc romfs <subcommand>`. Loaded only with the [cli] extra.
52
+ [project.entry-points."oaknut.command"]
53
+ romfs = "oaknut.romfs.cli:romfs"
54
+
55
+ [tool.setuptools.dynamic]
56
+ version = { attr = "oaknut.romfs.__version__" }
57
+
58
+ [tool.setuptools.packages.find]
59
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,46 @@
1
+ """Acorn ROM Filing System (ROMFS).
2
+
3
+ ROMFS is Acorn's filing system for *paged ROMs* — the sideways ROM and
4
+ cartridge format used on the BBC Micro and Acorn Electron. It stores
5
+ files in the same block layout as the Cassette Filing System (CFS): the
6
+ backing store is simply a linear ROM image rather than a tape, so a
7
+ file is a chain of CFS-format blocks (load/exec addresses, block number,
8
+ length, flag byte, header and data CRCs) preceded by a standard paged-ROM
9
+ service header.
10
+
11
+ Because the medium is read-only ROM the filing system is flat — there are
12
+ no directories — and a file's metadata is the Acorn load/exec pair plus a
13
+ lock bit, exactly as on cassette. This package adapts that on-ROM format
14
+ to the :mod:`oaknut.filesystem` extension contract so ROMFS images are
15
+ identifiable, listable and readable through the ``disc`` CLI alongside the
16
+ disc-based filing systems.
17
+
18
+ See ``docs/romfs-format-spec.md`` for the on-ROM byte layout and
19
+ ``docs/architecture.md`` for how the native API maps onto the
20
+ ``oaknut.filesystem`` plug-in interface.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ from oaknut.romfs.crc import crc16_ccitt
26
+ from oaknut.romfs.exceptions import (
27
+ CRCError,
28
+ NotAROMFSError,
29
+ ROMFSError,
30
+ ROMFullError,
31
+ TruncatedROMError,
32
+ )
33
+ from oaknut.romfs.romfs import ROMFS, ROMFSFile
34
+
35
+ __version__ = "12.3.0"
36
+
37
+ __all__ = [
38
+ "ROMFS",
39
+ "ROMFSFile",
40
+ "ROMFSError",
41
+ "NotAROMFSError",
42
+ "CRCError",
43
+ "TruncatedROMError",
44
+ "ROMFullError",
45
+ "crc16_ccitt",
46
+ ]
@@ -0,0 +1,142 @@
1
+ """The CFS block-header codec — parse and serialise one ROMFS block.
2
+
3
+ A ROMFS file is a chain of Cassette Filing System blocks. A *header
4
+ block* begins with the sync byte ``&2A`` followed by the fields modelled
5
+ by :class:`BlockHeader`; *continuation* blocks within a multi-block file
6
+ use a single ``&23`` marker instead of a header and are implicitly 256
7
+ data bytes. The filing system ends with a single ``&2B`` byte.
8
+
9
+ This module owns the byte-level header format (field layout, flag bits,
10
+ CRC placement). Walking a chain into files and assembling files into a
11
+ chain lives one layer up, in :mod:`oaknut.romfs.romfs`. See
12
+ ``docs/romfs-format-spec.md`` §2.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import struct
18
+ from dataclasses import dataclass
19
+
20
+ from oaknut.romfs.crc import crc16_ccitt
21
+ from oaknut.romfs.exceptions import CRCError, TruncatedROMError
22
+
23
+ #: A header block starts with this synchronisation byte (``'*'``).
24
+ SYNC_BYTE = 0x2A
25
+ #: A continuation (data-only) block within a multi-block file (``'#'``).
26
+ INTER_BLOCK_MARKER = 0x23
27
+ #: A single byte marking the end of the filing-system data (``'+'``).
28
+ END_OF_FILESYSTEM = 0x2B
29
+
30
+ #: Flag bit: this is the last block of its file.
31
+ FLAG_LAST = 0x80
32
+ #: Flag bit: this block carries no data (mkromfs sets it; Acorn does not —
33
+ #: test ``block_length == 0`` rather than this bit to detect an empty file).
34
+ FLAG_EMPTY = 0x40
35
+ #: Flag bit: the file is *RUN-only — a form of copy protection. The MOS
36
+ #: refuses to ``*LOAD`` / ``*EXEC`` / ``CHAIN`` it (only ``*RUN``); the OS
37
+ #: calls this "locked", but it is read-protection, not the disc filing
38
+ #: systems' delete-lock (see ``oaknut.file.Access.X``).
39
+ FLAG_RUN_ONLY = 0x01
40
+
41
+ #: Maximum file-name length (characters), excluding the NUL terminator.
42
+ MAX_NAME_LENGTH = 10
43
+ #: A full block carries this many data bytes; a continuation block always does.
44
+ BLOCK_DATA_SIZE = 256
45
+
46
+ # Fixed-size part of the header following the NUL-terminated name:
47
+ # load (4), exec (4), block number (2), block length (2), flag (1),
48
+ # end-of-file address (4). The two-byte header CRC follows.
49
+ _FIXED = struct.Struct("<IIHHBI")
50
+
51
+
52
+ @dataclass(frozen=True)
53
+ class BlockHeader:
54
+ """The header of one CFS block, in decoded form.
55
+
56
+ The multi-byte integer fields are stored little-endian on the ROM; the
57
+ trailing header CRC (handled by :meth:`to_bytes` / :meth:`parse`) is
58
+ stored big-endian. *end_address* is the paged-ROM address of the byte
59
+ just past this file — i.e. where the next file begins.
60
+ """
61
+
62
+ name: str
63
+ load_address: int
64
+ exec_address: int
65
+ block_number: int
66
+ block_length: int
67
+ flag: int
68
+ end_address: int
69
+
70
+ @property
71
+ def is_last(self) -> bool:
72
+ """Whether this is the final block of its file (flag bit 7)."""
73
+ return bool(self.flag & FLAG_LAST)
74
+
75
+ @property
76
+ def is_empty(self) -> bool:
77
+ """Whether the block carries no data (length 0)."""
78
+ return self.block_length == 0
79
+
80
+ @property
81
+ def is_run_only(self) -> bool:
82
+ """Whether the file is *RUN-only / copy-protected (flag bit 0)."""
83
+ return bool(self.flag & FLAG_RUN_ONLY)
84
+
85
+ def field_bytes(self) -> bytes:
86
+ """The header bytes the CRC is taken over: name through end address.
87
+
88
+ This is everything after the sync byte and before the header CRC.
89
+ """
90
+ return (
91
+ self.name.encode("latin-1")
92
+ + b"\x00"
93
+ + _FIXED.pack(
94
+ self.load_address,
95
+ self.exec_address,
96
+ self.block_number,
97
+ self.block_length,
98
+ self.flag,
99
+ self.end_address,
100
+ )
101
+ )
102
+
103
+ def to_bytes(self) -> bytes:
104
+ """The full on-ROM header: sync byte, fields, big-endian header CRC."""
105
+ fields = self.field_bytes()
106
+ crc = crc16_ccitt(fields)
107
+ return bytes([SYNC_BYTE]) + fields + crc.to_bytes(2, "big")
108
+
109
+ @classmethod
110
+ def parse(cls, buf, offset: int = 0) -> tuple["BlockHeader", int]:
111
+ """Parse a header block at *offset* (which must hold the sync byte).
112
+
113
+ Returns ``(header, data_offset)`` where *data_offset* is the offset
114
+ of the block's data, just past the two-byte header CRC. Raises
115
+ :class:`CRCError` if the stored header CRC does not match, and
116
+ :class:`TruncatedROMError` if the buffer ends mid-header.
117
+ """
118
+ view = memoryview(buf)
119
+ if offset >= len(view) or view[offset] != SYNC_BYTE:
120
+ raise TruncatedROMError(f"no block sync byte at offset {offset}")
121
+ name_start = offset + 1
122
+ nul = buf.find(b"\x00", name_start)
123
+ if nul < 0:
124
+ raise TruncatedROMError("unterminated block-header name")
125
+ fixed_start = nul + 1
126
+ crc_start = fixed_start + _FIXED.size
127
+ crc_end = crc_start + 2
128
+ if crc_end > len(view):
129
+ raise TruncatedROMError("block header runs past the end of the ROM")
130
+ name = bytes(view[name_start:nul]).decode("latin-1")
131
+ load, execa, block_number, block_length, flag, end_address = _FIXED.unpack(
132
+ view[fixed_start:crc_start]
133
+ )
134
+ stored_crc = int.from_bytes(view[crc_start:crc_end], "big")
135
+ computed = crc16_ccitt(view[name_start:crc_start])
136
+ if stored_crc != computed:
137
+ raise CRCError(
138
+ f"header CRC mismatch for {name!r}: "
139
+ f"stored &{stored_crc:04X}, computed &{computed:04X}"
140
+ )
141
+ header = cls(name, load, execa, block_number, block_length, flag, end_address)
142
+ return header, crc_end
@@ -0,0 +1,76 @@
1
+ """ROMFS contributed ``disc`` commands.
2
+
3
+ The ``disc romfs`` command group, contributed to the CLI on the
4
+ ``oaknut.command`` axis (see ``docs/dev/contributed-commands.md``). It holds
5
+ the ROMFS-specific paged-ROM *header* properties that do not fit the generic
6
+ mount model — the copyright string and the binary version byte — as simple
7
+ getters and setters on an existing image. Creating an image is the generic
8
+ ``disc create``; this group only queries and tweaks header metadata.
9
+
10
+ This module imports Click and is loaded only when ``oaknut-romfs`` is
11
+ installed with its ``[cli]`` extra; the library core never imports it.
12
+ Errors raised as :class:`~oaknut.romfs.exceptions.ROMFSError` are reported
13
+ by the CLI's shared error boundary.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from pathlib import Path
19
+
20
+ import click
21
+
22
+ _IMAGE = click.argument("image", type=click.Path(exists=True, dir_okay=False, path_type=Path))
23
+
24
+
25
+ @click.group()
26
+ def romfs() -> None:
27
+ """Acorn ROM Filing System administration."""
28
+
29
+
30
+ @romfs.command(name="get-copyright")
31
+ @_IMAGE
32
+ def get_copyright_command(image: Path) -> None:
33
+ """Print the paged-ROM copyright string of IMAGE."""
34
+ from oaknut.romfs.romfs import get_copyright
35
+
36
+ click.echo(get_copyright(image.read_bytes()))
37
+
38
+
39
+ @romfs.command(name="set-copyright")
40
+ @_IMAGE
41
+ @click.argument("copyright")
42
+ def set_copyright_command(image: Path, copyright: str) -> None:
43
+ """Set the paged-ROM copyright string of IMAGE (must begin "(C)").
44
+
45
+ A same-length string is written in place. A different length moves the
46
+ service handler, so the ROM is rebuilt — done only for a created-style
47
+ ROM (no language entry, nothing after the filing system); other ROMs are
48
+ refused to avoid disturbing their code.
49
+ """
50
+ from oaknut.romfs.romfs import set_copyright
51
+
52
+ image.write_bytes(set_copyright(image.read_bytes(), copyright))
53
+
54
+
55
+ @romfs.command(name="get-version")
56
+ @_IMAGE
57
+ def get_version_command(image: Path) -> None:
58
+ """Print the paged-ROM binary version byte of IMAGE."""
59
+ from oaknut.romfs.romfs import get_version
60
+
61
+ click.echo(get_version(image.read_bytes()))
62
+
63
+
64
+ @romfs.command(name="set-version")
65
+ @_IMAGE
66
+ @click.argument("version")
67
+ def set_version_command(image: Path, version: str) -> None:
68
+ """Set the paged-ROM binary version byte of IMAGE (0-255).
69
+
70
+ VERSION honours a base prefix, like the address commands: ``0x`` hex
71
+ (e.g. ``0x80``), ``0o`` octal, ``0b`` binary, or a plain decimal value.
72
+ """
73
+ from oaknut.file import parse_address
74
+ from oaknut.romfs.romfs import set_version
75
+
76
+ image.write_bytes(set_version(image.read_bytes(), parse_address(version)))
@@ -0,0 +1,34 @@
1
+ """The CRC used by ROMFS / Cassette Filing System blocks.
2
+
3
+ Both the block header CRC and the block data CRC are CRC-16/XMODEM:
4
+ polynomial ``0x1021``, initial value ``0x0000``, processed
5
+ most-significant-bit first with no input/output reflection. On the ROM the
6
+ 16-bit result is stored **big-endian** (most-significant byte first),
7
+ unlike the little-endian multi-byte integer fields of the header.
8
+
9
+ See ``docs/romfs-format-spec.md`` §3.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from collections.abc import Iterable
15
+
16
+ _POLYNOMIAL = 0x1021
17
+
18
+
19
+ def crc16_ccitt(data: Iterable[int]) -> int:
20
+ """Return the CRC-16/XMODEM of *data* (any iterable of byte values).
21
+
22
+ Accepts ``bytes``, ``bytearray``, ``memoryview`` or any iterable of
23
+ integers in ``0..255``. The empty input has CRC ``0x0000``; the
24
+ canonical check string ``b"123456789"`` has CRC ``0x31C3``.
25
+ """
26
+ crc = 0
27
+ for byte in data:
28
+ crc ^= byte << 8
29
+ for _ in range(8):
30
+ if crc & 0x8000:
31
+ crc = ((crc << 1) ^ _POLYNOMIAL) & 0xFFFF
32
+ else:
33
+ crc = (crc << 1) & 0xFFFF
34
+ return crc
@@ -0,0 +1,49 @@
1
+ """Exception hierarchy for oaknut.romfs.
2
+
3
+ All ROMFS-specific exceptions derive from :class:`ROMFSError`, which in
4
+ turn derives from the shared :class:`~oaknut.file.exceptions.FSError` base
5
+ (a ``DataError``). The CLI boundary reads an exception's ``exit_code`` to
6
+ decide its exit status, so each subclass pins a specific :class:`ExitCode`.
7
+
8
+ Hierarchy::
9
+
10
+ FSError (oaknut.file.exceptions, a DataError)
11
+ └── ROMFSError
12
+ ├── NotAROMFSError (ExitCode.DATA_ERR)
13
+ ├── CRCError (ExitCode.DATA_ERR)
14
+ ├── TruncatedROMError (ExitCode.DATA_ERR)
15
+ └── ROMFullError (ExitCode.CANT_CREATE)
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from exit_codes import ExitCode
21
+ from oaknut.file.exceptions import FSError
22
+
23
+
24
+ class ROMFSError(FSError):
25
+ """Base exception for all ROMFS errors."""
26
+
27
+
28
+ class NotAROMFSError(ROMFSError):
29
+ """The image is not a recognisable ROMFS paged ROM."""
30
+
31
+ _exit_code = ExitCode.DATA_ERR
32
+
33
+
34
+ class CRCError(ROMFSError):
35
+ """A stored header or data CRC does not match the computed value."""
36
+
37
+ _exit_code = ExitCode.DATA_ERR
38
+
39
+
40
+ class TruncatedROMError(ROMFSError):
41
+ """The ROM ends in the middle of a block header or its data."""
42
+
43
+ _exit_code = ExitCode.DATA_ERR
44
+
45
+
46
+ class ROMFullError(ROMFSError):
47
+ """The files no longer fit within the ROM's capacity."""
48
+
49
+ _exit_code = ExitCode.CANT_CREATE