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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@tybys/wz

v1.7.1

Published

MapleStory wz reader.

Downloads

44

Readme

node-wz

MapleStory wz reader for Node.js and browser.

Incompletely port from lastbattle/Harepacker-resurrected/MapleLib/WzLib.

API Documentation

Build

Environment:

  • Node.js v12+

  • CMake v3.6+

  • Emscripten toolchain latest

    • Set environment variable $EMSDK to emsdk path

    • Add $EMSDK and $EMSDK/upstream/emscripten to $PATH

  • Make for Windows (Windows only)

git clone https://github.com/toyobayashi/wz.git
cd wz
npm install
npm run build

Windows

npm install
npm run build

Example

npm install @tybys/wz

Node.js (v10.20+)

const path = require('path')
const {
  walkWzFileAsync,
  WzMapleVersion,
  WzObjectType,
  WzBinaryProperty,
  ErrorLogger
} = require('@tybys/wz')

/**
 * @param {string} wzFilePath - WZ file path
 * @param {WzMapleVersion} mapleVersion - MapleStory version
 * @param {string} dir - Output directory path
 */
async function saveSounds (wzFilePath, mapleVersion, dir) {
  let n = 0

  // let _doNotUseMe

  /**
   * @template {import('@tybys/wz').WzObject} T
   * @param {T} obj - wz object
   * @returns {Promise<boolean | undefined>}
   */
  async function callback (obj) {
    // obj is available only in this scope
    // _doNotUseMe = obj // ! do not do this
    if (obj.objectType === WzObjectType.Property && obj instanceof WzBinaryProperty) {
      const relativePath = path.win32.relative(wzFilePath, obj.fullPath).replace(/\\/g, '/')
      const file = path.join(dir, path.extname(relativePath) === '' ? `${relativePath}.mp3` : relativePath)
      console.log(`Saving ${path.resolve(file)}`)
      await obj.saveToFile(file)
      n++
    }
    return false // continue walking
  }

  await walkWzFileAsync(wzFilePath, mapleVersion, callback)

  console.log(`Total files: ${n}`)

  if (ErrorLogger.errorsPresent()) {
    ErrorLogger.saveToFile('WzError.log')
  }
}

saveSounds('C:\\Nexon\\MapleStory\\Sound.wz', WzMapleVersion.BMS, 'Sound')

Modern browser

Browser environment should be with ES2018+ and WebAssembly support.

<input type="file" name="sound" id="file">

<script src="node_modules/@tybys/wz/dist/wz.min.js"></script>
/// <reference path="node_modules/@tybys/wz/dist/wz.d.ts" />

(function () {
  const input = document.getElementById('file')

  input.addEventListener('change', async (e) => {
    const f = e.target.files[0] // Select the Sound.wz file

    await wz.walkWzFileAsync(f, wz.WzMapleVersion.BMS, async (obj) => {
      if (obj.objectType === wz.WzObjectType.Property && obj instanceof wz.WzBinaryProperty) {
        console.log(obj.fullPath)

        const buf = (await obj.getBytes(false)) // MP3 Uint8Array
        const blob = new Blob([buf.buffer], { type: 'audio/mp3' })
        const src = URL.createObjectURL(blob)
        const audio = new Audio()
        audio.src = src
        audio.play()

        await obj.saveToFile('1.mp3') // trigger download

        return true
      }
    })
  })
})()

Webpack

Add CopyWebpackPlugin to copy wz.wasm file

const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = {
  plugins: [
    new CopyWebpackPlugin({
      patterns: [
        { from: 'node_modules/@tybys/wz/dist/wz.wasm', to: '${the same place with output bundle}/wz.wasm' }
      ]
    })
  ],
  /* resolve: {
    alias: {
      '@tybys/binreader': '@tybys/binreader/lib/esm-modern/index.js'
    }
  } */
}
import { walkWzFileAsync, /* ... */ } from '@tybys/wz'

Old browser

For example IE11:

<!-- BigInt -->
<script>
if (typeof BigInt === 'undefined') {
  window.BigInt = function BigInt (n) {
    return n;
  };
}
</script>

<!-- document.currentScript -->
<script>
// https://github.com/amiller-gh/currentScript-polyfill/blob/master/currentScript.js
</script>

<!-- TextDecoder -->
<script src="https://cdn.jsdelivr.net/npm/text-encoding/lib/encoding-indexes.js"></script>
<script src="https://cdn.jsdelivr.net/npm/text-encoding/lib/encoding.js"></script>

<!-- ES6 globals -->
<script src="https://cdn.jsdelivr.net/npm/@babel/polyfill/dist/polyfill.min.js"></script>

<script src="node_modules/@tybys/wz/dist/wz.es5.min.js"></script>

Webpack

const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = {
  plugins: [
    new CopyWebpackPlugin({
      patterns: [
        { from: 'node_modules/@tybys/wz/dist/wz.js.mem', to: '${the same place with output bundle}/wz.js.mem' }
      ]
    })
  ],
  resolve: {
    alias: {
      '@tybys/wz': '@tybys/wz/lib/esm/index.js' // es5 output
    }
  }
}

Advanced

Though walkWzFileAsync() is easy to use, it is much more slower in browser than in Node.js. It is recommanded to use class API to do specific directory or image operation.

const { init, WzFile, WzMapleVersion, WzBinaryProperty, WzImage, WzDirectory, WzFileParseStatus, getErrorDescription } = require('@tybys/wz')

async function main () {
  // Must call init() first to initialize Webassembly
  // before calling other API in browser.
  // In nodejs it is just return Promise.resolve()
  await init()

  // Construct a WzFile object
  const wz = new WzFile('C:\\Nexon\\MapleStory\\Sound.wz', WzMapleVersion.BMS)

  const r = await wz.parseWzFile()
  if (r !== WzFileParseStatus.SUCCESS) {
    throw new Error(getErrorDescription(r))
  }

  // Access main directory
  /** @type {WzDirectory} */
  const mainDirectory = wz.wzDirectory // ! not null

  /** @type {WzImage | null} */
  const img = mainDirectory.at('Bgm50.img')
  if (img === null) throw new Error('404')

  // Parse the image before use it
  await img.parseImage()

  // Access image properties
  const props = img.wzProperties // getter returns Set<WzImageProperty>

  for (const prop of props) {
    if (prop instanceof WzBinaryProperty) {
      console.log(prop.fullPath)
      // do something
      // prop.saveToFile()
    }
  }
  wz.dispose()
}

main()