Client-Side Javascript NBT Decoder


After finishing up my NBT Decoder/Encoder for PHP, I thought that it mustn’t be too hard to implement the same thing in Javascript. At first, I thought it would be a bit more difficult because, so far as I knew, Javascript couldn’t handle reading files without first posting them to a server. Little did I know.

File API

One of the great things coming in “HTML5″ is the File API. Basically, this API provides a means to access local files without handing to first POST the data to a server. This allows for completely client-side web applications which can handle any number of file formats.

Thanks to this, this Javascript decoder requires absolutely zero server-side code.

Stumbling Blocks

Binary Data

Of course, the first thing I had to do before I could even think about writing some code was to find a decent binary unpacking library for Javascript. That wasn’t too hard, and the one I decided upon was Jonas Raoni Soares Silva’s Binary Parser library. It’s fairly straight-forward, and doesn’t include a lot of cruft.

GZip

Next, because Javascript lacks a native implementation of the GZip compression algorithm, I had to find a GZip library. Again, fairly straightforward problem, and I decided upon Ilmari Heikkinen (kig)’s tar.gz library, albeit with some trimming of unnecessary AJAX cruft.

64-bit Integers

Lastly, and most troublesome, I had to figure out a way to read in and handle 64-bit integers that NBT uses as TAG_Long. I had this same exact problem in PHP, so I already knew roughly how to deal with it. It took me a while to decide upon a library, but our good friend (apparently) Jonas Raoni Soares Silva, who had written the Binary Parser library, had also written a Big Number library that handles arbitrary-precision values quite elegantly.

For posterity, the reason that Javascript can’t handle 64-bit integers is that it uses 64-bit floating points for all of its numbers. While the number of bits might be the same, only 53 bits of data are used to represent the integer aspect of the number. To break that down, it’s 1 signing bit, 52 “fraction” bits (that’s the integer) and 11 exponent bits. If one were using an unsigned integer, I believe you get 53 bits.

Now, granted, Javascript *can* handle large numbers, as evidenced by Math.pow(2,64), but the problem is that they’re represented as floating points, and thus lose a great deal of accuracy at the advantage of precision.

Conclusion (The resulting script)

So, in Javascript, it’s actually as “easy” as, if not easier than, PHP to read in the NBT format. It’s also fairly fast and resource-friendly.

My resulting script will probably see a bit of cleaning-up, and I’m going to take a stab at adding in the ability to write the file back out.

Here’s the SVN Repository.

July 30th, 2011: I’ve moved this project to a github repository. All future updates will happen there.

http://jsfromhell.com/classes/binary-parserB
  1. #1 by Toenailsin on October 18, 2011 - 1:22 pm

    The File API has changed, meaning your test page no longer works. This is what i did to make it work:
    http://pastebin.com/Z0WYLrdz

  2. #2 by saviski on November 26, 2012 - 6:43 pm

    I need to change nbtproto.loadFile function

    var filestring = new StringStream(nbtfile.data.join(”));

    instead of nbtfile.data[0] to work with all schematics properly

(will not be published)
*