Node.js bindings for Blizzard MPQ archives, powered by StormLib.
node-stormlib exposes a small Node.js API for reading Blizzard MPQ archives,
including Warcraft III .w3m and .w3x maps. The native addon links against
the vendored StormLib source
tree.
| Capability | API |
|---|---|
| Read archive metadata | getArchiveInfo() |
| List files with StormLib masks | listFiles() |
| Check for a file | hasFile() |
| Inspect one file before reading | getFileInfo() |
Read archive files as Buffer objects |
readFile() |
| Read files from a worker thread | readFileAsync() |
| Stream archive files in chunks | createReadStream() |
| Extract archive files to disk | extractFile() |
| Create new MPQ archives | createArchive() |
| Add compressed local files | addFile() |
| Write compressed Buffer contents | writeFile() |
| Compact writable archives | compactArchive() |
- Node.js 18 or newer
- npm
- CMake
- A C++ compiler supported by CMake.js, such as Visual Studio Build Tools on Windows
npm installPublished packages use prebuilds/<platform>-<arch>/NodeStorm.node when a
matching addon is available. If no prebuild matches, the install script compiles
build/Release/NodeStorm.node with CMake.js. Set
NODE_STORMLIB_BUILD_FROM_SOURCE=1 to force a local build.
const storm = require('node-stormlib')
const archivePath = 'test/fixtures/maps/SaveAndLoad.w3x'
const info = storm.getArchiveInfo(archivePath)
const files = storm.listFiles(archivePath, 'war3map.*')
const fileInfo = storm.getFileInfo(archivePath, 'war3map.w3i')
const mapInfo = storm.readFile(archivePath, 'war3map.w3i')
console.log(info.fileCount)
console.log(files.map(file => file.name))
console.log(fileInfo.size)
console.log(mapInfo.length)Installing the package exposes an mpq command for common archive tasks.
npm install -g node-stormlib
mpq --help
mpq info map.w3x
mpq list map.w3x "war3map.*"
mpq extract map.w3x war3map.w3i out/war3map.w3i --root out
mpq unpack map.w3x out/map --max-bytes 10485760
mpq unpack map.w3x out/map --no-limitsCreate and compress archives from files or directories:
mpq create out/map.mpq --root out --overwrite --max-files 64
mpq add out/map.mpq assets/war3map.w3i war3map.w3i --source-root assets --compression zlib
mpq pack assets out/assets.mpq --root out --overwrite --compression zlib
mpq compact out/assets.mpq --root outUseful aliases include mpq ls, mpq x, mpq compress, and mpq decompress.
Bulk extraction validates archive entry names before writing. Read and write
commands default to a 512 MiB per-file limit, and list/unpack default to 10,000
entries. Use --max-bytes, --max-entries, or --no-limits to tune those
limits explicitly.
Opens an MPQ archive and returns basic metadata.
{
path: 'map.w3x',
fileCount: 15,
maxFileCount: 64,
sectorSize: 4096,
archiveSize: 15432
}Returns matching archive entries. The optional mask uses StormLib wildcard
matching, for example *.w3i or war3map.*.
const files = storm.listFiles('map.w3x', '*.w3i')
const firstTen = storm.listFiles('map.w3x', '*', { maxEntries: 10 })Each entry includes:
| Field | Description |
|---|---|
name |
Full archive path |
plainName |
Basename reported by StormLib |
size |
Uncompressed byte size |
compressedSize |
Stored byte size |
flags |
MPQ file flags |
locale |
File locale |
hashIndex |
Hash table index |
blockIndex |
Block table index |
Returns true when fileName exists in the archive.
storm.hasFile('map.w3x', 'war3map.w3i')Returns metadata for one archive file without reading the file contents.
const file = storm.getFileInfo('map.w3x', 'war3map.w3i')
console.log(file.size, file.compressedSize, file.flags)Reads a file from the archive and returns a Node.js Buffer.
const bytes = storm.readFile('map.w3x', 'war3map.w3i')
const capped = storm.readFile('map.w3x', 'war3map.w3i', { maxBytes: 1024 * 1024 })Reads a file on a worker thread and resolves with a Buffer.
const bytes = await storm.readFileAsync('map.w3x', 'war3map.w3i')Returns a Readable that emits the archive file contents in chunks. Pass
maxBytes to reject oversized files before chunk reads begin, and chunkSize
to tune the read size.
storm
.createReadStream('map.w3x', 'war3map.w3i', {
maxBytes: 1024 * 1024,
chunkSize: 64 * 1024,
})
.pipe(process.stdout)Extracts a file from the archive to outputPath and returns true on success.
Pass rootDir to reject writes outside an allowed directory.
storm.extractFile('map.w3x', 'war3map.w3i', 'out/war3map.w3i')
storm.extractFile('map.w3x', 'war3map.w3i', 'out/war3map.w3i', { rootDir: 'out' })StormLib failures include a stable JavaScript error.code such as
STORM_2, plus the numeric error.stormCode.
Creates a new MPQ archive. Pass rootDir to restrict the output path and
overwrite: true when replacing an existing archive is intended.
storm.createArchive('out/map.mpq', {
rootDir: 'out',
maxFileCount: 64,
version: 1,
})Adds a local file to an existing archive. By default files are compressed with zlib and existing entries are replaced.
storm.addFile('out/map.mpq', 'assets/war3map.w3i', 'war3map.w3i', {
rootDir: 'out',
sourceRootDir: 'assets',
maxBytes: 1024 * 1024,
compression: storm.compression.zlib,
})Writes a Buffer directly into an existing archive.
storm.writeFile('out/map.mpq', 'scripts/main.j', Buffer.from('function main takes nothing returns nothing'), {
rootDir: 'out',
compression: storm.compression.zlib,
})Compacts a writable archive after edits.
storm.compactArchive('out/map.mpq', { rootDir: 'out' })npm run build
npm test
npm run test:reviewnpm run buildcompiles the native addon and StormLib static library.npm testruns Node.js unit tests against real.w3xand.w3mfixtures.npm run test:reviewruns the regression tests that cover prior review findings.
.
|-- CMakeLists.txt Native addon build entry
|-- index.d.ts TypeScript declarations
|-- index.js JavaScript package entry
|-- main.cc Node-API wrapper around StormLib
|-- native.js Native addon loader with prebuild fallback
|-- read-worker.js Worker-thread helper for async reads
|-- test/fixtures/maps MPQ map fixtures tracked by Git LFS
|-- test/open-archive.test.js Unit tests for the public API
|-- test/review-findings.test.js Regression tests for review findings
|-- test/write-archive.test.js Unit tests for MPQ creation and compression
`-- third_party/StormLib Vendored StormLib source
StormLib is vendored from ladislav-zezula/StormLib at commit
448dd9a824f60d940a4627f5d2b8a7c31ee9287f. See
third_party/README.md and third_party/StormLib/LICENSE for details.
Small Warcraft III map fixtures live in test/fixtures/maps. Binary archive
formats such as .mpq, .w3m, .w3x, and .w3n are tracked with Git LFS.
This package is released under the MIT license.