A desktop GUI tool for browsing and inspecting the record-based game
database files (ADATA1, ADATA2) used by the Quest text-adventure
engine (Brendan Murray, 1992 / restarted 2023), as defined by
questdefs.h.
It lets you page through every record in the database, see each record broken down into its named fields (place descriptions, moves/exits, artefacts, gremlins, puzzle conditions, vocabulary keys, and state initialisation), and jump straight to another record just by double-clicking a field that references it.
(add a screenshot here once you've run it, e.g. screenshots/place_record.png)
- Background
- File format
- Features
- Requirements
- Installation
- Usage
- How the viewer decodes a record
- Known limitations
- Project structure
- License
Quest games store all their content — places, objects, creatures, text
and puzzle logic — in a flat, fixed-length record file. questdefs.h
defines the C structures the original engine uses to interpret those
records. This repository documents the on-disk format (reverse-engineered
from sample ADATA1 / ADATA2 files plus the header comments) and
provides a standalone Python viewer so the data can be inspected without
compiling or running the original game engine.
Confirmed from the sample data files:
-
Every record is exactly 100 ASCII characters, with no delimiters between fields or between records.
-
Character 100 (index 99) of every record is a single digit giving the record's type, matching the
RECORDTYPEenum inquestdefs.h:Digit Type Meaning 1 REC_CONTROLControl record — always record #0 2 REC_TEXTA block of on-screen text 3 REC_PLACEA location and its moves/exits 4 REC_ARTEFACTAn object and the actions that can be done with it 5 REC_GREMLINA creature and its behaviour 6 REC_CONDITIONA puzzle/logic test 7 REC_KEYSA block of vocabulary keywords 8 REC_STATEState-initialisation instructions run at game start other — Unused / free / never-allocated record slot -
The remaining 99 characters are that record's fields, packed back to back in the order given by the matching struct in
questdefs.h. -
A game can span two files (
ADATA1thenADATA2); record numbers are global — once you run past the last record inADATA1you continue intoADATA2.
Field widths used to split those 99 characters, derived from
questdefs.h's own helper macros (NUM reads a 2-digit number,
RECNUM reads a 4-digit number) and verified against sample data:
| C type | On-disk width | Notes |
|---|---|---|
RELREC (record pointer) |
4 characters | e.g. 0220 → record 220 |
short |
2 characters | |
char |
1 character | |
char[N] word |
N‑1 characters |
Last byte is the C string terminator, not stored on disk |
ACTION (flag + data) |
6 characters | 2-digit flag + 4-character data |
MOVE (in a move list) |
8 characters | 1-char type + 4-char target + 3-char action code |
TEXT and PLACE records match these widths exactly against the sample
files (confirmed via two clean 49-character text blocks, and real place
moves that decode to valid in-range record numbers with literal command
words). ARTEFACT, GREMLIN, CONDITION, KEYS and STATE records
use the same conventions for consistency but haven't been individually
verified byte-for-byte — see Known limitations.
A full field-by-field breakdown of every record type is documented in
Quest_Data_Format_Documentation.docx (included alongside this viewer).
questdefs.h and that document are the authoritative reference for
what each field means; this README just summarises the on-disk layout.
- Single-record view — one record at a time, laid out as a table of named fields rather than a wall of raw text.
- Automatic record-type detection — reads character 100 and displays the correct field layout for that record type.
- Dual-file support — load
ADATA1andADATA2together and navigate them as one continuous, globally-numbered set of records. Scrolling forwards or backwards across the boundary between the two files is handled automatically. - Field descriptions on hover — hover over any field name to see a
tooltip explaining what it means (sourced from
questdefs.h). - Jump to a referenced record — double-click a value that contains a record number (e.g. a place's text pointer, or a move's target) to jump straight to that record.
- Back navigation — retrace your steps through jumps with a Back button (or Alt+Left).
- Go to record # — type a record number and jump directly to it.
- Plain-text search — find the next record containing a given string, searching across both files.
- No unparsed data is ever hidden — if a record type's known fields don't consume all 99 characters, the remainder is shown in an "Unparsed / extra data" row instead of being silently dropped.
- Python 3.8+
- Tkinter (Python's standard GUI toolkit)
No third-party/pip packages are required — the viewer only uses Python's standard library.
Tkinter ships with the standard Python installer on Windows and macOS. On Linux it's often a separate OS package:
| Platform | How to get Tkinter |
|---|---|
| Windows / macOS (python.org installer) | Included by default |
| Ubuntu / Debian | sudo apt install python3-tk |
| Fedora | sudo dnf install python3-tkinter |
| Arch | sudo pacman -S tk |
To check whether it's already available:
python3 -c "import tkinter; print('Tkinter OK')"-
Clone or download this repository:
git clone https://github.com/<your-username>/<your-repo>.git cd <your-repo>
-
Make sure Python 3 and Tkinter are installed (see Requirements above).
-
That's it — there's nothing to
pip install.
Run the viewer and pass one or both data files as arguments:
python3 quest_viewer.py ADATA1 ADATA2You can also start it with no arguments and load files from the GUI:
python3 quest_viewer.pyThen use File > Open ADATA1 (file 1)... and File > Open ADATA2 (file 2)....
| Action | How |
|---|---|
| Next / previous record | Next ▷ / ◁ Prev buttons, or Page Up / Page Down |
| Jump to the first / last record | ⏮ First / Last ⏭ |
| Jump to a specific record number | Type it into Go to record # and press Enter (or click Go) |
| Jump to a record another field points to | Double-click that field's value |
| Return to where you jumped from | ◁ Back button, or Alt+Left |
| Search for text | Type into Find and press Enter (or click Find next) |
| See what a field means | Hover the mouse over its name |
Record numbers are global: if you go to record 2600 and ADATA1
only has 2,501 records, the viewer automatically shows record 99 of
ADATA2 (2600 − 2501). Scrolling with Next/Prev crosses this boundary
automatically in both directions.
For each record it:
- Works out which file and local record index a global record number maps to.
- Reads that record's 100 characters and looks at character 100 to determine the record type.
- Looks up the field list for that type (defined in
quest_viewer.py, matchingquestdefs.h) and slices the first 99 characters into fields in order, using the widths described above. - Displays each field with its name and current value; hovering a field name shows its description; any left-over or missing bytes are called out explicitly rather than guessed at or hidden.
- No delimiters exist in the file format, so field boundaries are
inferred from
questdefs.h's macros and cross-checked against sample data rather than being self-describing.TEXTandPLACErecords have been validated closely; other record types use the same conventions but may not be byte-perfect for every field, especially towards the end of longer records (e.g.ARTEFACT,GREMLIN). - The control record (record #0) appears to contain additional data
not described in
questdefs.h(e.g. what looks like a game title and some extra counters) — the fields that are defined decode correctly, and everything after them is shown as raw "Unparsed / extra data" rather than being misrepresented as something it isn't. - The viewer treats any record whose type digit doesn't match
1-8as an unused/free slot and shows its raw content only. - This is a read-only viewer — it does not edit or write back to the data files.
If you can independently confirm or correct any field widths (for
example by cross-referencing against the original Quest engine source or
a known-good save), the field tables near the top of quest_viewer.py
are the place to adjust them.
.
├── quest_viewer.py # the viewer application
├── Quest_Data_Format_Documentation.docx # full field-by-field reference (from questdefs.h)
├── questdefs.h # original C header defining the record structures
└── README.md
Add whatever license you'd like this project distributed under (e.g.
MIT) here. questdefs.h itself is Copyright © Brendan Murray,
1992-2023, all rights reserved — check with the original author before
redistributing it or the game data files.