@satgo1546
2015-08-17T14:37:03.000000Z
字数 4196
阅读 2117
Minecraft
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 TAG_Compound
.
A Named Tag has the following format:
byte tagType
TAG_String name
[payload]
The tagType
is a single byte defining the contents of the payload of the tag.
The 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 tagType
is TAG_End
, the name is skipped and assumed to be ""
.
The [payload] varies by tagType
.
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:
TYPE: 0
NAME: TAG_End
Payload: None.
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)
TYPE: 1
NAME: TAG_Byte
Payload: A single signed byte (8 bits)
TYPE: 2
NAME: TAG_Short
Payload: A signed short (16 bits, big endian)
TYPE: 3
NAME: TAG_Int
Payload: A signed short (32 bits, big endian)
TYPE: 4
NAME: TAG_Long
Payload: A signed long (64 bits, big endian)
TYPE: 5
NAME: TAG_Float
Payload: A floating point value (32 bits, big endian, IEEE 754-2008, binary32)
TYPE: 6
NAME: TAG_Double
Payload: A floating point value (64 bits, big endian, IEEE 754-2008, binary64)
TYPE: 7
NAME: TAG_Byte_Array
Payload:
TAG_Int
length
length
bytesTYPE: 8
NAME: TAG_String
Payload:
TAG_Short
length
length
bytesTYPE: 9
NAME: TAG_List
Payload:
TAG_Byte
tagId
TAG_Int
length
typeId
. The length of this array is length
Tags TYPE: 10
NAME: TAG_Compound
Payload:
TAG_End
is found.TAG_End
end
Notes:
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 TAG_Compound
The order of the tags is not guaranteed.
Decoding example:
(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 "hello world"
.
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 "Bananrama"
.
So now we know the TAG_Compound
contains a TAG_String
named "name"
with the content "Bananrama"
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
in the TAG_Compound
is over, and indeed all of the NBT file.
So we ended up with this:
TAG_Compound("hello world"): 1 entries
{
TAG_String("name"): Bananrama
}
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_Short("shortTest"): 32767
TAG_Long("longTest"): 9223372036854775807
TAG_Float("floatTest"): 0.49823147
TAG_String("stringTest"): HELLO WORLD THIS IS A TEST STRING ÅÄÖ!
TAG_Int("intTest"): 2147483647
TAG_Compound("nested compound test"): 2 entries
{
TAG_Compound("ham"): 2 entries
{
TAG_String("name"): Hampus
TAG_Float("value"): 0.75
}
TAG_Compound("egg"): 2 entries
{
TAG_String("name"): Eggbert
TAG_Float("value"): 0.5
}
}
TAG_List("listTest (long)"): 5 entries of type TAG_Long
{
TAG_Long: 11
TAG_Long: 12
TAG_Long: 13
TAG_Long: 14
TAG_Long: 15
}
TAG_Byte("byteTest"): 127
TAG_List("listTest (compound)"): 2 entries of type TAG_Compound
{
TAG_Compound: 2 entries
{
TAG_String("name"): Compound tag #0
TAG_Long("created-on"): 1264099775885
}
TAG_Compound: 2 entries
{
TAG_String("name"): Compound tag #1
TAG_Long("created-on"): 1264099775885
}
}
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]
TAG_Double("doubleTest"): 0.4931287132182315
}