A Z-Machine Emulator and Interpreter
Project description
Quendor
A Z-Machine emulator and interpreter, written in Python.
The Z-Machine is the virtual machine that Infocom designed in 1979 to run its text adventures, and which the interactive fiction community has used ever since. Quendor reads a compiled story file and executes it.
Status: pre-alpha. Quendor can load, validate, inspect, and disassemble story files; it cannot yet run them.
Requirements
- Python 3.12 or later
- uv for dependency and environment management
Installation
git clone https://github.com/jeffnyman/quendor-py.git
cd quendor-py
uv sync --all-groups
Usage
Quendor takes a story file -- a compiled Z-Machine game -- and, for now, inspects it. Running it comes later.
uv run quendor STORY.z3 # load and validate a story
uv run quendor STORY.z3 --header # display the header
uv run quendor STORY.z3 --disassemble # decode instructions
uv run quendor STORY.z3 --disassemble --start 6e9b --count 8
--headerreports the story's identity (Version, release, serial), its flags decoded into words, the memory map, table addresses, and where execution begins, laid out for comparison against ztools'infodump.--disassemblelists instructions in roughlytxd's layout: address, raw bytes, mnemonic, operands, store, branch, and inline text.--starttakes a hex byte address and defaults to the first instruction;--countdefaults to 16.- A
.zblorbpackage is unwrapped and its story loaded. A resource-only Blorb is rejected with a message saying what it holds instead -- and naming the story file sitting beside it, when there is an obvious one.
The package is also runnable as a module:
uv run python -m quendor STORY.z3
Development
All commands assume the environment created by uv sync --all-groups.
| Task | Command |
|---|---|
| Run the test suite | uv run pytest |
| Run tests without coverage | uv run pytest --no-cov |
| Lint | uv run ruff check . |
| Lint and autofix | uv run ruff check --fix . |
| Format | uv run ruff format . |
| Check formatting only | uv run ruff format --check . |
| Type check | uv run mypy |
| Build distributions | uv build |
Pre-commit hooks
Install the hooks once, after which lint, format, and type checks run on every commit, and commit messages are validated:
uv run pre-commit install
Every hook is a repo: local entry that runs its tool out of the project
environment via uv run, so pre-commit never clones hook repositories or
builds cached environments under ~/.cache/pre-commit. Tool versions have a
single source of truth: uv.lock.
To run every hook against the whole tree:
uv run pre-commit run --all-files
Commit messages
Commit messages follow Conventional Commits,
enforced at commit time by commitizen
through the commit-msg hook installed above:
feat: add object table parsing
fix(memory): reject story files shorter than the header
docs: explain the save file format
To check a message by hand, or to compose one interactively:
uv run cz check -m "feat: add object table parsing"
uv run cz commit
Because the history is machine-readable, commitizen can also derive the next version, tag it, and update the changelog once releases begin:
uv run cz bump
Optional test artifacts
Quendor does not depend on anything under entharion/. It is neither needed
to install the project nor to run the test suite, and CI does not fetch it.
Git leaves submodules empty unless asked, so a plain clone simply skips it.
It exists for hands-on work on the interpreter: story files to run, and tools to inspect what they contain.
| Path | Source | Contains |
|---|---|---|
entharion/ |
entharion | ZIL sources and Z-Machine reference material |
To fetch it, along with the frotz, ztools, and reform6 submodules it carries:
git submodule update --init --recursive
To discard it again, freeing the disk space without affecting the project:
git submodule deinit --all
None of it is treated as project source. Ruff excludes the directory via
extend-exclude, and mypy and pytest never see it, being scoped to src and
tests.
To move the pinned commit to the latest upstream:
git submodule update --remote entharion
git add entharion
git commit -m "Update entharion submodule"
Project conventions
- Layout. Source lives under
src/quendor, tests undertests/. Thesrclayout ensures tests exercise the installed package rather than the working directory. - Typing.
mypyruns in strict mode over bothsrcandtests, and the package ships apy.typedmarker so downstream consumers get its types. - Coverage. The suite is gated at 100% branch coverage. This is deliberate
for a project of this size; adjust
fail_underinpyproject.tomlif it stops being useful. - Spec citations. The
§references in code, docstrings, and output follow the HTML rendering of the Z-Machine Standard 1.1 vendored atentharion/specs/Z-Machine-Standard-1.1/. Other renderings of the same Standard, including the PDF beside it, number some paragraphs differently. - Line endings. LF everywhere except Windows script files, enforced by both
.gitattributesand.editorconfig.
License
Released under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file quendor-0.2.0.tar.gz.
File metadata
- Download URL: quendor-0.2.0.tar.gz
- Upload date:
- Size: 28.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9e154c712d9302db8d46a18fb1d42df8713d571a43729e938a9cfd1795909874
|
|
| MD5 |
1ecf63141418af5933f27f7595d4f041
|
|
| BLAKE2b-256 |
dd2c39394a9faed989ec66b5d8ee126dc9653b0435294eeef1ae173e321abf02
|
Provenance
The following attestation bundles were made for quendor-0.2.0.tar.gz:
Publisher:
release.yml on jeffnyman/quendor-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
quendor-0.2.0.tar.gz -
Subject digest:
9e154c712d9302db8d46a18fb1d42df8713d571a43729e938a9cfd1795909874 - Sigstore transparency entry: 2335019697
- Sigstore integration time:
-
Permalink:
jeffnyman/quendor-py@c10050ae463e0adbe60303bf426be1b2ad6bb338 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/jeffnyman
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c10050ae463e0adbe60303bf426be1b2ad6bb338 -
Trigger Event:
push
-
Statement type:
File details
Details for the file quendor-0.2.0-py3-none-any.whl.
File metadata
- Download URL: quendor-0.2.0-py3-none-any.whl
- Upload date:
- Size: 32.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f790735917e1394d629ec91c9b03d6011fc76c8e63a3bfb028a12b8e15a1d8b5
|
|
| MD5 |
3f50475141de35f74f9e19fde66de694
|
|
| BLAKE2b-256 |
ec5800a9b31cc86cd335c5f5210544b1bb418d5bf53406792d7ec970c6c2412d
|
Provenance
The following attestation bundles were made for quendor-0.2.0-py3-none-any.whl:
Publisher:
release.yml on jeffnyman/quendor-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
quendor-0.2.0-py3-none-any.whl -
Subject digest:
f790735917e1394d629ec91c9b03d6011fc76c8e63a3bfb028a12b8e15a1d8b5 - Sigstore transparency entry: 2335019718
- Sigstore integration time:
-
Permalink:
jeffnyman/quendor-py@c10050ae463e0adbe60303bf426be1b2ad6bb338 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/jeffnyman
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c10050ae463e0adbe60303bf426be1b2ad6bb338 -
Trigger Event:
push
-
Statement type: