wzlib-rs 0.1.0

MapleStory WZ file parser — Rust core with optional WASM bindings
Documentation

wzlib-rs

MapleStory WZ file parser and writer — Rust core compiled to WebAssembly with a TypeScript wrapper.

Reads and writes .wz and .ms archives used by MapleStory — directory trees, property trees, canvas images, sound, and video — all in the browser via WASM.

Features

  • Standard WZ — parse and save directory trees, IMG properties, version auto-detection, 64-bit support
  • Hotfix & List WZ — headerless Data.wz and pre-Big Bang List.wz path indices
  • MS archives — v220+ .ms files: v1 (Snow2) and v2 (ChaCha20) with auto-detection, full read/write for both versions
  • Canvas decoding — 14 pixel formats (DXT1/3/5, BC7, BGRA4444/8888, RGB565, etc.) → RGBA8888
  • Sound extraction — MP3/PCM for Web Audio playback
  • Video extraction — MCV container parsing with frame metadata
  • Encryption — GMS, EMS/MSEA, BMS/Classic with auto-detection
  • File saving — full WZ file packaging (header + directory + images), hotfix Data.wz, and MS file construction
  • Small footprint — ~100KB WASM binary (LTO, opt-level = "s")

Quick Start

Prerequisites

Build

# Build WASM package (--features wasm is required)

wasm-pack build --target web --out-dir ts-wrapper/wasm-pkg --features wasm


# Build TypeScript wrapper

cd ts-wrapper

npm install

npx tsc

Test

cargo test

cargo llvm-cov --lib              # coverage (requires cargo-llvm-cov)

Run Demo

node demo/serve.mjs

# Open http://localhost:8080

Drop a .wz or .ms file to explore directory trees, view images, play sounds, and inspect video metadata.

Usage

import { WzParser } from "wzlib";

const parser = await WzParser.create();

// Parse a .wz file
const wzData = new Uint8Array(await fetch("Map.wz").then(r => r.arrayBuffer()));
const root = parser.parseFile(wzData, "gms", 83);

// Navigate the tree
const mob = root.resolve("Mob/100100.img");
console.log(mob?.childNames);

// Decode a canvas
const rgba = parser.decodeCanvas(compressedPng, 64, 64, 2);
const imageData = parser.toImageData(rgba, 64, 64);
ctx.putImageData(imageData, 0, 0);

// Parse a .ms file (v220+)
const msData = new Uint8Array(await fetch("Data.ms").then(r => r.arrayBuffer()));
const entries = parser.parseMsFile(msData, "Data.ms");
const imgTree = parser.parseMsImage(msData, "Data.ms", 0);

// Edit and rebuild a hotfix Data.wz
const { properties, blobs } = parser.parseHotfixForEdit(wzData, "bms");
properties.find(p => p.name === "hp").value = 9999;
const saved = parser.buildImage(properties, blobs, "bms");

// Build a .ms file from entries (version: 1 = Snow2, 2 = ChaCha20)
const msEntries = [{ name: "Mob/test.img", entryKey: [...key16] }];
const msSaved = parser.buildMsFile("output.ms", "salt", msEntries, [imageBlob], 2);

Architecture

src/                  Rust WASM core
├── crypto/           AES, Snow2, ChaCha20, custom encryption, CRC32
├── wz/               WZ/MS/List file parsing + writing, MCV video, properties
├── image/            Pixel format decoders (DXT, BC7, etc.) → RGBA8888
└── wasm_api.rs       wasm-bindgen exports (parse + save)

ts-wrapper/src/       TypeScript wrapper
├── wz-parser.ts      High-level WzParser class
├── wz-node.ts        Tree navigation (resolve, walk)
└── types.ts          Shared type definitions

demo/                 Browser file explorer SPA
├── js/               Modular JS (state, tree, property, search, media…)
├── index.html        Entry page
└── styles.css        Styles

Ported From

Harepacker-resurrected — MapleLib WzComparerR2 - WzLib