NBT (Named Binary Tag) is a tag based binary format designed to carry large amounts of binary data with smaller amounts of additional data. An NBT file consists of a single GZIPped Named Tag of type
A Named Tag has the following format:
byte tagType TAG_String name [payload]
tagType is a single byte defining the contents of the payload of the tag.
name is a descriptive name, and can be anything (eg "cat", "banana", "Hello World!"). It has nothing to do with the
tagType. The purpose for this
name is to name tags so parsing is easier and can be made to only look for certain recognized tag names. Exception: If
TAG_End, the name is skipped and assumed to be
The [payload] varies by
Note that ONLY Named Tags carry the name and tagType data. Explicitly identified Tags (such as TAG_String above) only contains the payload.
The tag types and respective payloads are:
Note: This tag is used to mark the end of a list.
Cannot be named! If type 0 appears where a Named Tag is expected, the name is assumed to be
(In other words, this Tag is always just a single 0 byte when named, and nothing in all other cases)
Payload: A single signed byte (8 bits)
Payload: A signed short (16 bits, big endian)
Payload: A signed short (32 bits, big endian)
Payload: A signed long (64 bits, big endian)
Payload: A floating point value (32 bits, big endian, IEEE 754-2008, binary32)
Payload: A floating point value (64 bits, big endian, IEEE 754-2008, binary64)
typeId. The length of this array is
If there's a nested
TAG_Compound within this tag, that one will also have a
TAG_End, so simply reading until the next
TAG_End will not work.
The names of the named tags have to be unique within each
The order of the tags is not guaranteed.
(Use http://www.minecraft.net/docs/test.nbt to test your implementation)
First we start by reading a Named Tag.
After unzipping the stream, the first byte is a 10. That means the tag is a
TAG_Compound (as expected by the specification).
The next two bytes are 0 and 11, meaning the name string consists of 11 UTF-8 characters. In this case, they happen to be
That means our root tag is named
"hello world". We can now move on to the payload.
From the specification, we see that
TAG_Compound consists of a series of Named Tags, so we read another byte to find the tagType.
It happens to be an 8. The name is 4 letters long, and happens to be
"name". Type 8 is
TAG_String, meaning we read another two bytes to get the length,
then read that many bytes to get the contents. In this case, it's
So now we know the
TAG_Compound contains a
"name" with the content
We move on to reading the next Named Tag, and get a 0. This is
TAG_End, which always has an implied name of
"". That means that the list of entries
TAG_Compound is over, and indeed all of the NBT file.
So we ended up with this:
TAG_Compound("hello world"): 1 entries
For a slightly longer test, download http://www.minecraft.net/docs/bigtest.nbt
You should end up with this:
TAG_Compound("Level"): 11 entries
TAG_String("stringTest"): HELLO WORLD THIS IS A TEST STRING ÅÄÖ!
TAG_Compound("nested compound test"): 2 entries
TAG_Compound("ham"): 2 entries
TAG_Compound("egg"): 2 entries
TAG_List("listTest (long)"): 5 entries of type TAG_Long
TAG_List("listTest (compound)"): 2 entries of type TAG_Compound
TAG_Compound: 2 entries
TAG_String("name"): Compound tag #0
TAG_Compound: 2 entries
TAG_String("name"): Compound tag #1
TAG_Byte_Array("byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))"): [1000 bytes]