npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dm2-tools

v0.2.1

Published

WebAssembly bindings exposing the Dungeon Master II parsers to JavaScript via wasm-bindgen.

Downloads

489

Readme

dm2-wasm

Browser bindings for the Dungeon Master II asset parsers in this workspace. It wraps the dm2-saves, dm2-dungeon, dm2-graphics, dm2-music, and dm2-ftl crates behind a small #[wasm_bindgen] API so a JS/TypeScript front end can parse, convert, and re-encode game files entirely client-side — no server round-trip, no game data ever leaves the browser.

The crate is a thin JS-marshalling layer. Every export takes bytes (Uint8Array) or text (string) and returns bytes, text, or a boolean; none of it does its own parsing — that all lives in the wrapped crates.

Install

npm install dm2-tools

The npm package is called dm2-tools (matching the Python distribution) while the crate is dm2-wasm; build-npm.sh below reconciles the two. It is built for the bundler target, so Vite, webpack and friends import it with no explicit initialisation step:

import { saveToJson } from "dm2-tools";

Build

For the npm package:

crates/dm2-wasm/build-npm.sh          # add --dev to skip wasm-opt

That wraps wasm-pack build --target bundler --release and patches the generated package.json — see the comments in the script for why both are needed. The result lands in crates/dm2-wasm/pkg, ready for npm publish.

For a page with no bundler at all, build the web target directly:

wasm-pack build crates/dm2-wasm --target web

--target web produces an ES module (pkg/dm2_wasm.js + pkg/dm2_wasm_bg.wasm) that a page can import directly, at the cost of an explicit init() call — see the usage example below. Add --dev to either while iterating, to skip the wasm-opt optimization pass and get a much faster, unoptimized build.

The crate also builds and tests as an ordinary native Rust crate (cargo build -p dm2-wasm, cargo test -p dm2-wasm) — the #[wasm_bindgen] attributes compile inertly off the wasm32 target, so the underlying parsers stay covered by the workspace's normal native test suite.

JS API

All exports throw a JS Error (via JsError) on failure — wrap calls in try/catch. Byte parameters/returns are Uint8Array; on the JS side pass a Uint8Array for any &[u8] parameter below.

| Export | Params | Returns | Purpose | | --- | --- | --- | --- | | saveToJson | bytes: Uint8Array | string | Detect + parse a savegame and dump it to the self-contained, lossless JSON representation (round-trips byte-exact via saveFromJson). | | saveFromJson | json: string | Uint8Array | Rebuild savegame bytes from a saveToJson dump. No external files needed. | | saveInfoJson | bytes: Uint8Array | string | Detect + parse a savegame and dump it to a lossy, human-readable gameplay-only JSON view (inventory, stats, party state). | | dungeonToJson | bytes: Uint8Array | string | Detect + parse a dungeon.dat and dump it to the self-contained, lossless JSON representation (round-trips byte-exact via dungeonFromJson). | | dungeonFromJson | json: string | Uint8Array | Rebuild dungeon.dat bytes from a dungeonToJson dump. No external files needed. | | dungeonSkprojectJson | bytes: Uint8Array | string | Detect + parse a dungeon.dat and dump it to the lossy, skproject-compatible JSON representation. | | graphicsHeaderJson | bytes: Uint8Array | string | Parse a graphics.dat header (auto-detecting byte order) and dump a small JSON summary (format version, byte order, entry count). | | graphicsDecodePng | bytes: Uint8Array, cls1: number, cls2: number, cls4: number | Uint8Array | Parse a graphics.dat archive and decode the image at class tuple (cls1, cls2, 0x01, cls4) to a truecolor PNG. Read-only decoder. | | musicDetect | bytes: Uint8Array | string | Sniff a standalone music file's container format: "hmp", "mod", "modplayer41a", or "smf". | | musicHmpToSmf | bytes: Uint8Array | Uint8Array | Convert a DOS HMI/HMP music stream to a Standard MIDI File. Read-only decoder. | | musicRenderModWav | bytes: Uint8Array | Uint8Array | Render an Amiga ProTracker module (packed with The Player 4.1A, or plain) to a stereo WAV file. Read-only decoder. | | musicSndToWav | bytes: Uint8Array | Uint8Array | Decode a Mac resource-fork 'snd ' resource payload to a mono WAV file. Read-only decoder. | | ftlInfoJson | bytes: Uint8Array | string | Parse an FTL 68k module container (Sega CD / Amiga) and dump a small JSON summary (hunk sizes, checksum status). Read-only decoder. | | ftlRoundtripOk | bytes: Uint8Array | boolean | Parse an FTL 68k module and re-emit it, checking the result is byte-exact with the input. Read-only decoder. |

Only the save and dungeon pairs are read/write; graphics, music, and FTL exports are read-only decoders with no matching "from JSON"/encode entry point.

Usage example

A minimal round-trip savegame edit, entirely in the browser:

import { saveToJson, saveFromJson } from "dm2-tools";

// `saveBytes` is a Uint8Array read from a file input, drag-and-drop, etc.
const dumpJson = saveToJson(saveBytes);
const dump = JSON.parse(dumpJson);

// Edit whatever field the JSON dump exposes, e.g. the first champion's
// current hit points:
dump.gameplay.champions[0].cur_hp = 999;

const editedBytes = saveFromJson(JSON.stringify(dump));
// `editedBytes` is a Uint8Array — byte-exact `SkSave#.dat` content aside
// from the edited field, ready to offer back for download.

Against a web-target build from a checkout the same code needs the module initialised first — import init, { saveToJson } from "./pkg/dm2_wasm.js" followed by await init() — after which the calls are identical.

The dump/reconstruct pair is self-contained: saveToJson's output carries everything saveFromJson needs, so no other file has to be fetched or uploaded alongside it. The same pattern applies to dungeonToJson / dungeonFromJson for dungeon.dat files.