• v0.2.1 9ed7aebfbb

    v0.2.1
    All checks were successful
    CI / stable (pull_request) Successful in 1m36s
    CI / python bindings (3.11) (pull_request) Successful in 38s
    CI / msrv (1.90) (pull_request) Successful in 42s
    CI / audit (pull_request) Successful in 23s
    CI / msrv (1.90) (push) Successful in 43s
    CI / audit (push) Successful in 24s
    CI / stable (push) Successful in 1m37s
    CI / python bindings (3.11) (push) Successful in 39s
    Release / wheel + c bundle (manylinux x86_64) (push) Successful in 1m44s
    Release / build (cli + windows wheel + c bundle) (push) Successful in 2m14s
    Release / publish (push) Successful in 23s
    Stable

    jqueuniet released this 2026-09-05 08:29:21 +02:00 | 4 commits to main since this release

    Signed by jqueuniet
    SSH key fingerprint: SHA256:hjUxou9yR5AKBQ8zK2FkTEp12iDlEomvb63VBpXVwN4

    Added

    • C bundles — tagged releases now attach dm2-ffi-<ver>-x86_64-linux-gnu
      and -x86_64-windows-gnu archives, each with the cbindgen header, the shared
      library, the crate README carrying the Dm2Buffer ownership contract, and
      both licenses. The Linux one is built in the manylinux image, so it needs
      only glibc 2.28. The 30-40 MB static archive is left out: it would grow the
      compressed bundle elevenfold to duplicate what the shared library provides.

    Changed

    • dm2-wasm — prepared for npm. build-npm.sh builds the package for the
      bundler target and publishes it under the name dm2-tools, matching the
      PyPI distribution; the crate ships copies of the two licenses, which
      wasm-pack bundles, build-npm.sh lists in files so npm actually packs
      them, and CI diffs against the originals; and wasm-opt now optimises for
      size (-Oz). CI gained a wasm32-unknown-unknown check,
      since a host build compiles the #[wasm_bindgen] layer inertly and cannot
      catch a break in it.
    • dm2-wasm — no longer published to crates.io (publish = false, and
      0.2.0 deleted there). The bindings target JavaScript, which reaches them
      through wasm-pack output rather than cargo, and the crates.io copy
      documented nothing: docs.rs builds for x86-64, where every #[wasm_bindgen]
      export is compiled out, so the page listed only WasmError. The crate stays
      in the workspace and is built and tested exactly as before. Its unused
      [workspace.dependencies] entry, which carried a version requirement no
      crate consumed, is dropped.
    • Python bindingsrequires-python raised to >=3.11. 3.9 went EOL in
      October 2025 and 3.10 follows in October 2026, so the old >=3.9 floor
      advertised runtimes that no longer get security fixes and that the suite
      could not import anyway. Installing already requires a Rust toolchain to
      build the cdylib, so a distro system Python was never the constraint. The
      test helper now parses the manifests with the stdlib tomllib instead of a
      regex. Adds a classifiers list so a PyPI upload carries version, platform
      and Typing :: Typed trove tags.
    • CI — new python job installs the bindings on a standalone CPython 3.11
      (fetched by a pinned, hash-checked uv) and runs their pytest suite, so the
      declared floor and the hatchling cdylib-bundling hook are both exercised.
      Previously nothing in CI touched bindings/python at all.
    • Python bindings — the wheel is now tagged py3-none-<platform> instead
      of cp3XX-cp3XX-<platform>. Nothing links against the CPython C API, so the
      old tag demanded a separate wheel per interpreter minor for no reason; one
      wheel per platform now installs on every supported Python. The build hook
      also clears its _lib staging directory, which previously let a cross-built
      wheel ship a second, wrong-platform library.
    • Python bindings — packaging metadata filled in: [project.urls],
      keywords, and the two LICENSE-* files, which the wheel previously omitted
      despite declaring License-Expression.
    • Release — tagged builds now attach Python wheels:
      py3-none-manylinux_2_28_x86_64 from a new wheel-linux job in the
      manylinux image (auditwheel relabels the linux_x86_64 tag PyPI rejects),
      and py3-none-win_amd64 cross-built with mingw alongside the CLI. Wheels
      only, no sdist — an sdist of bindings/python cannot build, since the
      cdylib it compiles lives in the cargo workspace above it. macOS and aarch64
      Linux wheels are not built; see docs/development.md.
    • Release — the workflow is now two build jobs feeding a publish job
      through Forgejo's upload-artifact action, instead of one job that built
      and published in sequence. Assets are attached only once every build has
      succeeded, so a failure can no longer leave a published tag missing one;
      SHA256SUMS.txt covers the wheels as well as the CLI archives; and a
      workflow_dispatch run leaves every asset downloadable from the run page.

    Fixed

    • Python bindingsbindings/python/pyproject.toml still declared
      0.1.1, so the 0.2.0 wheel carried pip metadata a version behind the
      cdylib it bundles, and the suite's version assertion (hardcoded to the old
      string) failed against it. The package version now tracks the workspace, and
      the test derives both sides from the manifests instead of pinning a literal.
    • Python bindingstests/test_roundtrip.py annotated a helper with a
      PEP 604 union but omitted from __future__ import annotations, so importing
      the module raised TypeError on the 3.9 and 3.10 that requires-python
      claims to support. The suite only ever ran on 3.11+.
    Downloads
  • v0.2.0 810bc844f4

    v0.2.0
    All checks were successful
    CI / msrv (1.90) (pull_request) Successful in 45s
    CI / audit (pull_request) Successful in 22s
    CI / stable (pull_request) Successful in 1m17s
    CI / audit (push) Successful in 22s
    CI / stable (push) Successful in 1m16s
    CI / msrv (1.90) (push) Successful in 46s
    Release / build + release (push) Successful in 1m28s
    Stable

    jqueuniet released this 2026-09-04 19:23:59 +02:00 | 13 commits to main since this release

    Signed by jqueuniet
    SSH key fingerprint: SHA256:hjUxou9yR5AKBQ8zK2FkTEp12iDlEomvb63VBpXVwN4

    Added

    • dm2-wasm — browser bindings exposing the parser crates to JavaScript
      via wasm-bindgen. All parser crates already compile to
      wasm32-unknown-unknown unchanged, so this is a marshalling layer rather
      than a port: a pure fn(&[u8]) -> Result<_, WasmError> core covered by
      native tests, one unified WasmError whose Display is the JS-facing
      message, and #[wasm_bindgen] passthroughs with camelCase names. Saves and
      dungeons get a byte-exact self-contained JSON round-trip plus the lossy
      read views; graphics, music, and FTL are read-only decoders (header summary
      and image→PNG; detect, HMP→SMF, MOD→WAV, snd →WAV; FTL container info and
      round-trip). Byte order is auto-detected inside the crate.
    • dm2-convert — wasm-free crate holding the conversion functions and
      their unified error, extracted out of dm2-wasm (now a re-export plus a
      wasm-bindgen shim). Adds resource-fork listing and extraction.
    • dm2-ffi — C ABI over the parser crates: a stateless
      Dm2Buffer { data, len, error } return convention with a single
      dm2_buffer_free, 16 panic-guarded exports plus dm2_version, and a
      committed cbindgen-generated header guarded by a drift-check test.
    • Python bindings (bindings/python, pip-installable dm2-tools) —
      ctypes over dm2-ffi with a Pythonic dict/bytes API and a Dm2Error
      exception, a hatchling build hook that builds and bundles the dm2-ffi
      cdylib into a platform-tagged wheel, and a pytest suite mirroring the ABI
      round-trip and read proofs.
    • Savegame JSONdm2 dump now emits savegame JSON in three flavours:
      gameplay-only by default (typed header, SGB, globals, champions, effects,
      and timers, each with a raw escape hatch), --self-contained to embed the
      full bitstream so the JSON reloads without a dungeon.dat / graphics.dat,
      and --full (with --gdat and --dungeon) for a field-driven dump whose
      record_stream is editable. Both self-contained and field-driven dumps
      reconstruct byte-identical originals, gated over all 4 DOS retail slots and
      all 14 Mac retail saves. Reconstruction is hardened against hand-edited or
      untrusted JSON: typed errors instead of panics, and a 16 MiB allocation cap.
    • dm2 dump --format dungeon-lossless — self-contained lossless
      dungeon.dat JSON. DungeonDump carries every typed section as editable
      fields plus the tile-data-block tail as a base64 seed, so reconstruct()
      needs no external files, and parse → JSON → reconstruct is byte-exact over
      every dungeon fixture in both byte orders (including the FM-Towns JP
      off-by-one tail padding). Format, schema, length, and allocation guards
      return typed errors instead of panicking. The lossy skproject dump is
      unchanged.
    • serde Serialize/Deserialize derives across the dm2-saves
      RecordStream graph and every owned section type of the dm2-dungeon
      graph, so both are usable as data models outside the CLI.
    • Forgejo issue templates (feature / bug / research) that preset the type/*
      labels and prompt for area and port.

    Changed

    • Documentation: a Releasing runbook in the development guide covering the
      dependency-ordered publish; the "Known DM2 ports" table moved into
      docs/sources/overview.md as a "Platform families" section; the Sega CD
      in-game SFX location documented as resolved (they are cls3=0x02 dtSnd
      records in graphics.dat, missed by the 6-byte DOS/Mac SFX-framing scan).
    • Documentation and source comments no longer reference untracked working
      directories, so a fresh clone has no dangling pointers. The one
      load-bearing tool that lived there, extract_mac_object_masks.py, moved
      into tooling/oracle-compare/.
    • Corrected the documented Mac champion mask: its field bit widths are the
      same as DOS (HP/MP 10-bit, stamina 14-bit, food/water 16-bit, popcount
      1045), not widened/narrowed as previously claimed, and the -2 shift drops
      damagesuffered, not bodyflag. Three stale rows in the DOS mask table
      (0x29, food, water) were fixed alongside.
    • Dependencies updated.

    Removed

    • ROADMAP.md, in favour of issues and milestones on the project forge.
      Per-format and per-port reference material stays in docs/.

    Fixed

    • A corrupt savegame is now reported with the parser's own diagnostic instead
      of a generic "could not identify" / "not a recognised DM2 savegame". The
      savegame family gate (the looks_like_* header fingerprint) is now
      separated from the full parse, so info, verify, dump, raw-extract,
      and the save subcommands surface e.g. "sksave truncated in map_table:
      need 704 bytes, got 14" for a truncated save. Matches the behaviour the
      magic-bearing families (graphics.dat, FTL, Mac resource forks) already
      had.
    Downloads
  • v0.1.1 486b3f8566

    v0.1.1
    All checks were successful
    CI / msrv (1.90) (pull_request) Successful in 42s
    CI / stable (pull_request) Successful in 1m1s
    CI / audit (pull_request) Successful in 19s
    CI / msrv (1.90) (push) Successful in 44s
    CI / stable (push) Successful in 1m1s
    CI / audit (push) Successful in 18s
    Release / build + release (push) Successful in 1m28s
    Stable

    jqueuniet released this 2026-07-23 14:10:31 +02:00 | 53 commits to main since this release

    Signed by jqueuniet
    SSH key fingerprint: SHA256:hjUxou9yR5AKBQ8zK2FkTEp12iDlEomvb63VBpXVwN4

    Fixed

    • Dependency issue on internal dev crate
    Downloads
  • v0.1.0 7a82edbc6a

    v0.1.0
    All checks were successful
    CI / msrv (1.90) (pull_request) Successful in 44s
    CI / stable (pull_request) Successful in 58s
    CI / audit (pull_request) Successful in 15s
    CI / msrv (1.90) (push) Successful in 42s
    CI / stable (push) Successful in 57s
    CI / audit (push) Successful in 15s
    Release / build + release (push) Successful in 1m26s
    Stable

    jqueuniet released this 2026-07-23 13:34:48 +02:00 | 56 commits to main since this release

    Signed by jqueuniet
    SSH key fingerprint: SHA256:hjUxou9yR5AKBQ8zK2FkTEp12iDlEomvb63VBpXVwN4

    First public release: a Rust workspace of parsers, decoders, and a dm2
    command-line tool for the assets of Dungeon Master II: The Legend of
    Skullkeep
    , covering the PC (DOS), Macintosh, Amiga, and Sega CD editions in
    both little- and big-endian byte orders.

    Added

    • dm2 CLI — inspect, extract, convert, repack, render, and edit game
      assets:
      • info — print format metadata for a single asset file.
      • extract — unpack graphics.dat / dungeon.dat into a directory, with
        indexed-PNG transparency detection, font-sheet spacing control,
        console-family lighting (--style / --light), per-language text
        filtering, and additive type filters (--sfx, --music, --palette,
        --font, …).
      • dump — convert a recognised file to JSON.
      • convert — transcode music between formats (HMP↔MIDI, …) and savegames
        between DOS and Mac layouts.
      • repack — reassemble an original install into a target engine's expected
        layout.
      • render — render music to 16-bit audio: Mac via the SoundMusicSys synth
        (22254 Hz), DOS via OPL2/OPL3 (49716 Hz), Amiga via a Paula-faithful
        ProTracker replayer (44100 Hz). WAV to a file, raw s16le PCM to stdout.
      • verify — round-trip parse → emit → byte-compare to prove writer fidelity.
      • save — read and edit savegames.
    • dm2-graphics — parser and writer for graphics.dat (format 3/4/5, LE
      and BE), including image, palette, font, text, and sound-effect payloads.
    • dm2-dungeon — parser and writer for dungeon.dat (LE and BE variants).
    • dm2-saves — parser and writer for savegame files (SkSave.dat,
      sksave.bak), across DOS and Mac variants.
    • dm2-music — decoders for DM2 music and audio: HMI/HMP (DOS), Amiga MOD,
      and the Macintosh classic resource fork ('Midi' SMF, 'snd ' Sound Manager
      samples, plus AppleDouble v2 / MacBinary II unwrappers).
    • mod-audio — ProTracker (Amiga MOD) rendering with Paula-faithful hard
      panning.
    • dm2-ftl — parser for the FTL 68k module container used by the Sega CD
      and Amiga ports (and the DM/CSB console ports), including the zero-run
      HUNK_DATA and nibble-stream HUNK_CODE decompressors and all four
      container checksums.
    • dm2-wav — WAV writing helpers for the audio formats the game shipped.
    • dm2-binrw — workspace-internal binrw glue: trait-bounded
      parse / write / to-bytes helpers shared by the binary parsers.
    • dm2-test-fixtures — workspace-internal test helpers that resolve paths
      against fixtures/ and skip silently when a fixture is absent.
    • Per-crate examples/ demonstrating minimal use of each library crate.
    • Engineering hygiene: CI, cargo-deny supply-chain checks, Kaitai Struct
      format specs, rustdoc pages, and fuzz targets.

    Notes

    • The reusable synth crates opl-audio and sms-audio were split out of this
      workspace and are published separately on crates.io; dm2-tools consumes
      them as external dependencies.
    Downloads