Tooling to work with asset files from the 1995 video game Dungeon Master II: Legend of Skullkeep
  • Rust 96.3%
  • Kaitai Struct 1.1%
  • Python 1%
  • GLSL 0.5%
  • Shell 0.4%
  • Other 0.6%
Find a file
Johann Queuniet 2b24cba906
All checks were successful
CI / msrv (1.90) (push) Successful in 40s
CI / stable (push) Successful in 59s
CI / audit (push) Successful in 19s
Merge remote-tracking branch 'origin/main'
2026-07-22 22:57:39 +02:00
.claude refactor(dm2-saves): remove committed spike; document gdat-mask assumption; assert no width-change 2026-06-27 16:23:54 +02:00
.forgejo/workflows chore: remove codeberg-specific configuration 2026-07-22 22:28:30 +02:00
.idea chore: register mod-audio source folders in the IDE IML 2026-07-16 06:50:07 +02:00
crates docs: fix medium doc/code inconsistencies across formats 2026-07-22 07:16:22 +02:00
docs docs: fix low-severity doc/code inconsistencies 2026-07-22 07:22:34 +02:00
fixtures test(fixtures): add DM2 save-file fixtures (88 files) 2026-07-20 06:54:41 +02:00
formats docs: fix low-severity doc/code inconsistencies 2026-07-22 07:22:34 +02:00
fuzz test(fuzz): add four coverage-gap targets, seeds, and round-trip asserts 2026-07-22 15:40:52 +02:00
scratch docs(scratch): correct stale extract-script note (no type-9 special-case; alignment artifact) 2026-06-28 09:01:56 +02:00
scripts feat(saves): decode DOS Beta v0.9 gameplay (party + champions) 2026-06-30 20:28:05 +02:00
tooling docs: fix medium doc/code inconsistencies across formats 2026-07-22 07:16:22 +02:00
.gitignore chore(fixtures): allow committing save files 2026-07-20 06:54:29 +02:00
Cargo.lock fix(dm2-cli): guard non-empty output dirs and quiet broken-pipe exits (#244) 2026-07-16 15:47:12 +02:00
Cargo.toml fix(dm2-cli): guard non-empty output dirs and quiet broken-pipe exits (#244) 2026-07-16 15:47:12 +02:00
deny.toml ci/forgejo-pipeline (#2) 2026-05-04 10:50:15 +02:00
LICENSE-APACHE Initial commit 2026-04-26 23:09:03 +02:00
LICENSE-MIT Initial commit 2026-04-26 23:09:03 +02:00
README.md docs: fix medium doc/code inconsistencies across formats 2026-07-22 07:16:22 +02:00
ROADMAP.md test(fuzz): add four coverage-gap targets, seeds, and round-trip asserts 2026-07-22 15:40:52 +02:00

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 17; 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.

Roadmap

ROADMAP.md tracks what is done versus what remains across all crates and infrastructure. Update it as items move from one column to the other rather than letting it go stale.

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 Codeberg CI host quirks.

Format documentation

Format notes live under docs/formats/:

  • graphics-dat.md — the asset archive
  • dungeon-dat.md — level data
  • music.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 US md.dat / DOS songlist.dat / Amiga CD.DAT music-routing tables
  • saves.mdSkSave#.dat savegames; see its capability matrix for per-port support and conversion
  • qstrat-dat.md — Hint Oracle data (Mac US + Amiga; DOS embeds it inside graphics.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.