- Rust 95.4%
- Python 1.7%
- Kaitai Struct 1.1%
- Shell 0.5%
- GLSL 0.5%
- Other 0.7%
|
All checks were successful
CI / msrv (1.90) (pull_request) Successful in 42s
CI / audit (pull_request) Successful in 19s
CI / stable (pull_request) Successful in 1m42s
CI / python bindings (3.11) (pull_request) Successful in 46s
CI / msrv (1.90) (push) Successful in 40s
CI / audit (push) Successful in 19s
CI / stable (push) Successful in 1m42s
CI / python bindings (3.11) (push) Successful in 44s
The mechanical half of a release was four hand edits with a documented trap in
each: bump `[workspace.package]`, bump nine inter-crate requirements, bump
`bindings/python/pyproject.toml` — which `cargo set-version` cannot reach, and
whose omission shipped a 0.2.0 wheel disagreeing with its own library — and
then retitle the changelog section, open a fresh one and rewrite two link
references. `cargo release <version>` now does all of it, dry-run by default.
It is configured not to tag, push or publish. Tagging triggers the workflow
that publishes to three registries, so it stays a separate act after the
prepare commit has been reviewed.
Two details the dry run turned up rather than the documentation:
`pre-release-replacements` is inherited by every workspace member and its
paths resolve against that member's directory, so a workspace-level entry goes
looking for `crates/dm2-binrw/CHANGELOG.md`. The replacements therefore sit on
dm2-cli with `../../` paths, and run once.
`{{tag_name}}` is not available to replacements. Used there it is written into
the file literally, behind a warning that is easy to miss in the output — so
the link references use `v{{version}}`.
Verified by running it for real against a throwaway clone: the commit touches
exactly Cargo.toml, Cargo.lock, pyproject.toml and CHANGELOG.md; all ten
version references move; no tag and no push; and the resulting changelog
matches what was written by hand for 0.2.1.
|
||
|---|---|---|
| .claude | ||
| .forgejo | ||
| .idea | ||
| bindings/python | ||
| crates | ||
| docs | ||
| fixtures | ||
| formats | ||
| fuzz | ||
| scripts | ||
| tooling | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CHANGELOG.md | ||
| deny.toml | ||
| LICENSE-APACHE | ||
| LICENSE-MIT | ||
| README.md | ||
dm2-tools
A Cargo workspace of Rust libraries and a CLI for working with Dungeon Master II asset files (the 1995 Legend of Skullkeep).
The goal is to provide reusable, well-tested parsers for the formats the game
ships in — graphics.dat, dungeon.dat, music data, savegames — and a
single dm2 binary that uses them to extract, convert, and repack assets
into the layout downstream consumers expect (notably the unofficial
skproject C++ port).
This is tooling only. The runtime engine is out of scope here; if you want to play the game, point the converted output at an engine that knows how to load it.
Layout
crates/
├── dm2-graphics/ library: graphics.dat parser/writer (formats 4/5, LE+BE) + 107 SFX decoder
├── dm2-dungeon/ library: dungeon.dat parser/writer (LE+BE)
├── dm2-music/ library: HMP (DOS), MOD (Amiga), SMF + 'snd ' (Mac resource fork)
├── sms-audio/ library: SoundMusicSys (Mac) song renderer — SMF + Sound Manager sample bank to PCM
├── opl-audio/ library: pure-Rust OPL2 (YM3812) FM renderer — timed MIDI events + AdLib bank to PCM
├── mod-audio/ library: ProTracker (Amiga MOD) renderer, wraps xmrsplayer with Paula-faithful hard panning
├── dm2-saves/ library: SkSave#.dat / .bak full decode/encode (header, gameplay sections, record stream phases 1–7; DOS + Mac, bit-exact field re-encode)
├── dm2-ftl/ library: FTL 68k module container parser (Sega CD / Amiga) — decompressors + all four checksums
├── dm2-wav/ workspace-internal: shared mono 8-bit WAV writer (consumed by dm2-graphics + dm2-music)
├── dm2-binrw/ workspace-internal: binrw glue (parse/write_to/to_bytes helpers)
├── dm2-test-fixtures/ workspace-internal: fixture-path macros
└── dm2-cli/ binary `dm2`: info, extract, dump, convert, repack, verify, save, render
Editing savegames from the CLI is documented in
docs/cli-save-editing.md.
Library crates accept &[u8] or impl Read + Seek; only the CLI does path
I/O. This keeps parsers cheap to test and fuzz.
Build
cargo build --workspace
cargo test --workspace
cargo run -p dm2-cli -- --help
Format specs (Kaitai Struct)
formats/ holds machine-readable .ksy specifications
for the DM2 formats we have firm byte-level knowledge of —
graphics.dat (format 5, LE+BE), dungeon.dat header (LE+BE), Mac
md.dat, DOS songlist.dat, sksave#.dat header. The specs are
verified end-to-end against fixtures via the Python kaitaistruct
runtime and serve as the canonical declarative reference.
The Rust crates parse via binrw rather
than codegen — see formats/README.md for the
rationale and the hand-sync convention between .ksy and #[binrw]
structs.
Project status
Phases 1–6 have landed (every format decodes and round-trips; the CLI,
tooling, CI and the first crates.io release are done). Remaining and future
work is tracked as issues and milestones on the Forgejo project, not in
this repo — thematic milestones (cinematics, Amiga deep-dive, bindings, the
GUI editors, …) sliced by area/, type/, and port/ labels, with a
Kanban board for status. Per-format and per-port reference lives under
docs/ (start at docs/sources/overview.md).
Contributing
docs/development.md collects the working
conventions: the branch/commit workflow, the pre-push gate that mirrors
CI, markdown/doc house style, the source-of-truth hierarchy for
reverse-engineering claims, and the Forgejo CI runner quirks.
Format documentation
Format notes live under docs/formats/:
graphics-dat.md— the asset archivedungeon-dat.md— level datamusic.md— DOS HMP, Amiga MOD (Player 4.1A packed), Mac US SMF-via-SoundMusicSys, Sega CD / Mac JP Red Book CD-DA, and the Mac USmd.dat/ DOSsonglist.dat/ AmigaCD.DATmusic-routing tablessaves.md—SkSave#.datsavegames; see its capability matrix for per-port support and conversionqstrat-dat.md— Hint Oracle data (Mac US + Amiga; DOS embeds it insidegraphics.dat)mac-resources.md— Mac resource fork reference (the format the entire Mac US soundtrack is delivered in; Mac JP uses Red Book CD-DA instead)
Per-source notes live under docs/sources/; see
docs/sources/overview.md for the
cross-port doc index, the 16-fixture attribute matrix, and the
asset-family / DUNGEON.DAT lineage clusters. Each port has its own
extraction-recipe doc next to the overview.
Operational tooling notes — which external CLI tools (binwalk,
hexyl, rizin, sox, fluidsynth, kaitai-struct-compiler, …)
help with which task — live in
docs/tooling.md. None are workspace build
dependencies; they are agent-driveable companions for asset
extraction and verification work.
External references are collected in
docs/external-references.md; format
docs cite them by short name. Currently registered: [DMWEB],
[DMFORUM], [GREATSTONE], [DM2EDIT], [SKPROJECT], [KKDF2],
[IM], [RFC1740], [HMI], [SMF], [SMS], [SCUMMVM].
Test fixtures
No game data is committed. See fixtures/README.md for
the layout you can drop your own legitimately-owned copies into so integration
tests run against real data.
License
Dual-licensed under MIT or Apache-2.0 at your option. See LICENSE-MIT and
LICENSE-APACHE.