Merge remote-tracking branch 'origin/master'

This commit is contained in:
Tom
2020-10-06 00:13:24 -07:00
5 changed files with 349 additions and 259 deletions

View File

@ -1,4 +1,53 @@
# NBT
This package implement the Named Binary Tag format of Minecraft.
This package implement the Named Binary Tag format of Minecraft. It supports all formats
of NBT including compact arrays for longs.
# Usage
For the following NBT tag:
```
TAG_Compound("hello world") {
TAG_String('name'): 'Bananrama'
}
```
To read and write would look like:
```go
package main
import "bytes"
import "github.com/Tnze/go-mc/nbt"
type Compound struct {
Name string `nbt:"name"` // Note that if name is private (name), the field will not be used
}
func main() {
var out bytes.Buffer
banana := Compound{Name: "Bananrama"}
_ = nbt.Marshal(&out, banana)
var rama Compound
_ = nbt.Unmarshal(out.Bytes(), &rama)
}
```
# Struct field tags
There are two tags supported:
- nbt
- nbt_type
The `nbt` tag is used to change the name of the NBT Tag field, whereas the `nbt_type`
tag is used to enforce a certain NBT Tag type when it is ambiguous.
For example:
```go
type Compound struct {
LongArray []int64
LongList []int64 `nbt_type:"list"` // forces a long list instead of a long array
}
```
# Docs
[![GoDoc](https://godoc.org/github.com/Tnze/go-mc/nbt?status.svg)](https://godoc.org/github.com/Tnze/go-mc/nbt)

View File

@ -5,12 +5,26 @@ import (
"io"
"math"
"reflect"
"strings"
)
var (
ErrMustBeStruct = errors.New("a compound can only be a struct")
)
func Marshal(w io.Writer, v interface{}) error {
return NewEncoder(w).Encode(v)
}
func MarshalCompound(w io.Writer, v interface{}, rootTagName string) error {
enc := NewEncoder(w)
val := reflect.ValueOf(v)
if val.Kind() != reflect.Struct {
return ErrMustBeStruct
}
return enc.marshal(val, TagCompound, rootTagName)
}
type Encoder struct {
w io.Writer
}
@ -21,166 +35,87 @@ func NewEncoder(w io.Writer) *Encoder {
func (e *Encoder) Encode(v interface{}) error {
val := reflect.ValueOf(v)
return e.marshal(val, "")
return e.marshal(val, getTagType(val.Type()), "")
}
func (e *Encoder) marshal(val reflect.Value, tagName string) error {
switch vk := val.Kind(); vk {
default:
return errors.New("unknown type " + vk.String())
func (e *Encoder) marshal(val reflect.Value, tagType byte, tagName string) error {
if err := e.writeHeader(val, tagType, tagName); err != nil {
return err
}
return e.writeValue(val, tagType)
}
case reflect.Uint8:
if err := e.writeTag(TagByte, tagName); err != nil {
return err
}
func (e *Encoder) writeHeader(val reflect.Value, tagType byte, tagName string) (err error) {
if tagType == TagList {
eleType := getTagType(val.Type().Elem())
err = e.writeListHeader(eleType, tagName, val.Len())
} else {
err = e.writeTag(tagType, tagName)
}
return err
}
func (e *Encoder) writeValue(val reflect.Value, tagType byte) error {
switch tagType {
default:
return errors.New("unsupported type " + val.Type().Kind().String())
case TagByte:
_, err := e.w.Write([]byte{byte(val.Uint())})
return err
case reflect.Int16, reflect.Uint16:
if err := e.writeTag(TagShort, tagName); err != nil {
return err
}
case TagShort:
return e.writeInt16(int16(val.Int()))
case reflect.Int32, reflect.Uint32:
if err := e.writeTag(TagInt, tagName); err != nil {
return err
}
case TagInt:
return e.writeInt32(int32(val.Int()))
case reflect.Float32:
if err := e.writeTag(TagFloat, tagName); err != nil {
return err
}
case TagFloat:
return e.writeInt32(int32(math.Float32bits(float32(val.Float()))))
case reflect.Int64, reflect.Uint64:
if err := e.writeTag(TagLong, tagName); err != nil {
return err
}
return e.writeInt64(int64(val.Int()))
case reflect.Float64:
if err := e.writeTag(TagDouble, tagName); err != nil {
return err
}
case TagLong:
return e.writeInt64(val.Int())
case TagDouble:
return e.writeInt64(int64(math.Float64bits(val.Float())))
case reflect.Array, reflect.Slice:
case TagByteArray, TagIntArray, TagLongArray:
n := val.Len()
switch val.Type().Elem().Kind() {
case reflect.Uint8: // []byte
if err := e.writeTag(TagByteArray, tagName); err != nil {
return err
}
if err := e.writeInt32(int32(val.Len())); err != nil {
return err
}
if err := e.writeInt32(int32(n)); err != nil {
return err
}
if tagType == TagByteArray {
_, err := e.w.Write(val.Bytes())
return err
} else {
for i := 0; i < n; i++ {
v := val.Index(i).Int()
case reflect.Int32:
if err := e.writeTag(TagIntArray, tagName); err != nil {
return err
}
if err := e.writeInt32(int32(n)); err != nil {
return err
}
for i := 0; i < n; i++ {
if err := e.writeInt32(int32(val.Index(i).Int())); err != nil {
return err
var err error
if tagType == TagIntArray {
err = e.writeInt32(int32(v))
} else if tagType == TagLongArray {
err = e.writeInt64(v)
}
}
case reflect.Int64:
if err := e.writeTag(TagLongArray, tagName); err != nil {
return err
}
if err := e.writeInt32(int32(n)); err != nil {
return err
}
for i := 0; i < n; i++ {
if err := e.writeInt64(val.Index(i).Int()); err != nil {
return err
}
}
case reflect.Int16:
if err := e.writeListHeader(TagShort, tagName, val.Len()); err != nil {
return err
}
for i := 0; i < n; i++ {
if err := e.writeInt16(int16(val.Index(i).Int())); err != nil {
return err
}
}
case reflect.Float32:
if err := e.writeListHeader(TagFloat, tagName, val.Len()); err != nil {
return err
}
for i := 0; i < n; i++ {
if err := e.writeInt32(int32(math.Float32bits(float32(val.Index(i).Float())))); err != nil {
return err
}
}
case reflect.Float64:
if err := e.writeListHeader(TagDouble, tagName, val.Len()); err != nil {
return err
}
for i := 0; i < n; i++ {
if err := e.writeInt64(int64(math.Float64bits(val.Index(i).Float()))); err != nil {
return err
}
}
case reflect.String:
if err := e.writeListHeader(TagString, tagName, n); err != nil {
return err
}
for i := 0; i < n; i++ {
// Write length of this string
s := val.Index(i).String()
if err := e.writeInt16(int16(len(s))); err != nil {
return err
}
// Write string
if _, err := e.w.Write([]byte(s)); err != nil {
return err
}
}
case reflect.Struct, reflect.Interface:
if err := e.writeListHeader(TagCompound, tagName, n); err != nil {
return err
}
for i := 0; i < n; i++ {
elemVal := val.Index(i)
if val.Type().Elem().Kind() == reflect.Interface {
elemVal = reflect.ValueOf(elemVal.Interface())
}
err := e.marshal(elemVal, "")
if err != nil {
return err
}
}
default:
return errors.New("unknown type " + val.Type().String() + " slice")
}
case reflect.String:
if err := e.writeTag(TagString, tagName); err != nil {
return err
case TagList:
for i := 0; i < val.Len(); i++ {
arrVal := val.Index(i)
err := e.writeValue(arrVal, getTagType(arrVal.Type()))
if err != nil {
return err
}
}
case TagString:
if err := e.writeInt16(int16(val.Len())); err != nil {
return err
}
_, err := e.w.Write([]byte(val.String()))
return err
case reflect.Struct:
if err := e.writeTag(TagCompound, ""); err != nil {
return err
case TagCompound:
if val.Kind() == reflect.Interface {
val = reflect.ValueOf(val.Interface())
}
n := val.NumField()
@ -191,12 +126,8 @@ func (e *Encoder) marshal(val reflect.Value, tagName string) error {
continue // Private field
}
tagName := f.Name
if tag != "" {
tagName = tag
}
err := e.marshal(val.Field(i), tagName)
tagProps := parseTag(f, tag)
err := e.marshal(val.Field(i), tagProps.Type, tagProps.Name)
if err != nil {
return err
}
@ -207,6 +138,65 @@ func (e *Encoder) marshal(val reflect.Value, tagName string) error {
return nil
}
func getTagType(vk reflect.Type) byte {
switch vk.Kind() {
case reflect.Uint8:
return TagByte
case reflect.Int16, reflect.Uint16:
return TagShort
case reflect.Int32, reflect.Uint32:
return TagInt
case reflect.Float32:
return TagFloat
case reflect.Int64, reflect.Uint64:
return TagLong
case reflect.Float64:
return TagDouble
case reflect.String:
return TagString
case reflect.Struct, reflect.Interface:
return TagCompound
case reflect.Array, reflect.Slice:
switch vk.Elem().Kind() {
case reflect.Uint8: // Special types for these values
return TagByteArray
case reflect.Int32:
return TagIntArray
case reflect.Int64:
return TagLongArray
default:
return TagList
}
default:
return TagNone
}
}
type tagProps struct {
Name string
Type byte
}
func parseTag(f reflect.StructField, tagName string) tagProps {
result := tagProps{}
result.Name = tagName
if result.Name == "" {
result.Name = f.Name
}
nbtType := f.Tag.Get("nbt_type")
result.Type = getTagType(f.Type)
if strings.Contains(nbtType, "list") {
if IsArrayTag(result.Type) {
result.Type = TagList // for expanding the array to a standard list
} else {
panic("list is only supported for array types (byte, int, long)")
}
}
return result
}
func (e *Encoder) writeTag(tagType byte, tagName string) error {
if _, err := e.w.Write([]byte{tagType}); err != nil {
return err
@ -233,11 +223,6 @@ func (e *Encoder) writeListHeader(elementType byte, tagName string, n int) (err
return nil
}
func (e *Encoder) writeNamelessTag(tagType byte, tagName string) error {
_, err := e.w.Write([]byte{tagType})
return err
}
func (e *Encoder) writeInt16(n int16) error {
_, err := e.w.Write([]byte{byte(n >> 8), byte(n)})
return err

View File

@ -56,6 +56,19 @@ func TestMarshal_FloatArray(t *testing.T) {
}
}
func TestMarshal_String(t *testing.T) {
v := "Test"
out := []byte{TagString, 0x00, 0x00, 0, 4,
'T', 'e', 's', 't'}
var buf bytes.Buffer
if err := Marshal(&buf, v); err != nil {
t.Error(err)
} else if !bytes.Equal(buf.Bytes(), out) {
t.Errorf("output binary not right: got % 02x, want % 02x ", buf.Bytes(), out)
}
}
func TestMarshal_InterfaceArray(t *testing.T) {
type Struct1 struct {
Val int32
@ -76,16 +89,15 @@ func TestMarshal_InterfaceArray(t *testing.T) {
want: []byte{
TagList, 0x00, 0x00 /*no name*/, TagCompound, 0, 0, 0, 2,
// 1st element
TagCompound, 0x00, 0x00, /*no name*/
TagInt, 0x00, 0x03, 'V', 'a', 'l', 0x00, 0x00, 0x00, 0x03, // 3
TagEnd,
// 2nd element
TagCompound, 0x00, 0x00, /*no name*/
TagFloat, 0x00, 0x03, 'V', 'a', 'l', 0x3e, 0x99, 0x99, 0x9a, // 0.3
TagEnd,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &bytes.Buffer{}
@ -105,24 +117,39 @@ func TestMarshal_StructArray(t *testing.T) {
Val int32
}
type Struct2 struct {
T int32
Ele Struct1
}
type StructCont struct {
V []Struct2
}
tests := []struct {
name string
args []Struct1
args StructCont
want []byte
}{
{
name: "One element struct array",
args: []Struct1{{3}, {-10}},
args: StructCont{[]Struct2{{3, Struct1{3}}, {-10, Struct1{-10}}}},
want: []byte{
TagList, 0x00, 0x00 /*no name*/, TagCompound, 0, 0, 0, 2,
// 1st element
TagCompound, 0x00, 0x00, /*no name*/
TagCompound, 0x00, 0x00,
TagList, 0x00, 0x01, 'V', TagCompound, 0, 0, 0, 2,
// Struct2
TagInt, 0x00, 0x01, 'T', 0x00, 0x00, 0x00, 0x03,
TagCompound, 0x00, 0x03, 'E', 'l', 'e',
TagInt, 0x00, 0x03, 'V', 'a', 'l', 0x00, 0x00, 0x00, 0x03, // 3
TagEnd,
TagEnd,
// 2nd element
TagCompound, 0x00, 0x00, /*no name*/
TagInt, 0x00, 0x01, 'T', 0xff, 0xff, 0xff, 0xf6,
TagCompound, 0x00, 0x03, 'E', 'l', 'e',
TagInt, 0x00, 0x03, 'V', 'a', 'l', 0xff, 0xff, 0xff, 0xf6, // -10
TagEnd,
TagEnd,
TagEnd,
},
},
}

View File

@ -22,8 +22,13 @@ const (
TagCompound
TagIntArray
TagLongArray
TagNone = 0xFF
)
func IsArrayTag(ty byte) bool {
return ty == TagByteArray || ty == TagIntArray || ty == TagLongArray
}
type DecoderReader = interface {
io.ByteScanner
io.Reader

View File

@ -3,6 +3,7 @@ package nbt
import (
"bytes"
"compress/gzip"
"io/ioutil"
"reflect"
"testing"
)
@ -60,111 +61,102 @@ func TestUnmarshal_simple(t *testing.T) {
}
}
func TestUnmarshal_bitTest(t *testing.T) {
// Generated by vscode-hexdump
data := []byte{
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xed, 0x54, 0xcf, 0x4f, 0x1a, 0x41,
0x14, 0x7e, 0xc2, 0x02, 0xcb, 0x96, 0x82, 0xb1,
0xc4, 0x10, 0x63, 0xcc, 0xab, 0xb5, 0x84, 0xa5,
0xdb, 0xcd, 0x42, 0x11, 0x89, 0xb1, 0x88, 0x16,
0x2c, 0x9a, 0x0d, 0x1a, 0xd8, 0xa8, 0x31, 0x86,
0xb8, 0x2b, 0xc3, 0x82, 0x2e, 0xbb, 0x66, 0x77,
0xb0, 0xf1, 0xd4, 0x4b, 0x7b, 0x6c, 0x7a, 0xeb,
0x3f, 0xd3, 0x23, 0x7f, 0x43, 0xcf, 0xbd, 0xf6,
0xbf, 0xa0, 0xc3, 0x2f, 0x7b, 0x69, 0xcf, 0xbd,
0xf0, 0x32, 0xc9, 0xf7, 0xe6, 0xbd, 0x6f, 0xe6,
0x7b, 0x6f, 0x26, 0x79, 0x02, 0x04, 0x54, 0x72,
0x4f, 0x2c, 0x0e, 0x78, 0xcb, 0xb1, 0x4d, 0x8d,
0x78, 0xf4, 0xe3, 0x70, 0x62, 0x3e, 0x08, 0x7b,
0x1d, 0xc7, 0xa5, 0x93, 0x18, 0x0f, 0x82, 0x47,
0xdd, 0xee, 0x84, 0x02, 0x62, 0xb5, 0xa2, 0xaa,
0xc7, 0x78, 0x76, 0x5c, 0x57, 0xcb, 0xa8, 0x55,
0x0f, 0x1b, 0xc8, 0xd6, 0x1e, 0x6a, 0x95, 0x86,
0x86, 0x0d, 0xad, 0x7e, 0x58, 0x7b, 0x8f, 0x83,
0xcf, 0x83, 0x4f, 0x83, 0x6f, 0xcf, 0x03, 0x10,
0x6e, 0x5b, 0x8e, 0x3e, 0xbe, 0xa5, 0x38, 0x4c,
0x64, 0xfd, 0x10, 0xea, 0xda, 0x74, 0xa6, 0x23,
0x40, 0xdc, 0x66, 0x2e, 0x69, 0xe1, 0xb5, 0xd3,
0xbb, 0x73, 0xfa, 0x76, 0x0b, 0x29, 0xdb, 0x0b,
0xe0, 0xef, 0xe8, 0x3d, 0x1e, 0x38, 0x5b, 0xef,
0x11, 0x08, 0x56, 0xf5, 0xde, 0x5d, 0xdf, 0x0b,
0x40, 0xe0, 0x5e, 0xb7, 0xfa, 0x64, 0xb7, 0x04,
0x00, 0x8c, 0x41, 0x4c, 0x73, 0xc6, 0x08, 0x55,
0x4c, 0xd3, 0x20, 0x2e, 0x7d, 0xa4, 0xc0, 0xc8,
0xc2, 0x10, 0xb3, 0xba, 0xde, 0x58, 0x0b, 0x53,
0xa3, 0xee, 0x44, 0x8e, 0x45, 0x03, 0x30, 0xb1,
0x27, 0x53, 0x8c, 0x4c, 0xf1, 0xe9, 0x14, 0xa3,
0x53, 0x8c, 0x85, 0xe1, 0xd9, 0x9f, 0xe3, 0xb3,
0xf2, 0x44, 0x81, 0xa5, 0x7c, 0x33, 0xdd, 0xd8,
0xbb, 0xc7, 0xaa, 0x75, 0x13, 0x5f, 0x28, 0x1c,
0x08, 0xd7, 0x2e, 0xd1, 0x59, 0x3f, 0xaf, 0x1d,
0x1b, 0x60, 0x21, 0x59, 0xdf, 0xfa, 0xf1, 0x05,
0xfe, 0xc1, 0xce, 0xfc, 0x9d, 0xbd, 0x00, 0xbc,
0xf1, 0x40, 0xc9, 0xf8, 0x85, 0x42, 0x40, 0x46,
0xfe, 0x9e, 0xeb, 0xea, 0x0f, 0x93, 0x3a, 0x68,
0x87, 0x60, 0xbb, 0xeb, 0x32, 0x37, 0xa3, 0x28,
0x0a, 0x8e, 0xbb, 0xf5, 0xd0, 0x69, 0x63, 0xca,
0x4e, 0xdb, 0xe9, 0xec, 0xe6, 0xe6, 0x2b, 0x3b,
0xbd, 0x25, 0xbe, 0x64, 0x49, 0x09, 0x3d, 0xaa,
0xbb, 0x94, 0xfd, 0x18, 0x7e, 0xe8, 0xd2, 0x0e,
0xda, 0x6f, 0x15, 0x4c, 0xb1, 0x68, 0x3e, 0x2b,
0xe1, 0x9b, 0x9c, 0x84, 0x99, 0xbc, 0x84, 0x05,
0x09, 0x65, 0x59, 0x16, 0x45, 0x00, 0xff, 0x2f,
0x28, 0xae, 0x2f, 0xf2, 0xc2, 0xb2, 0xa4, 0x2e,
0x1d, 0x20, 0x77, 0x5a, 0x3b, 0xb9, 0x8c, 0xca,
0xe7, 0x29, 0xdf, 0x51, 0x41, 0xc9, 0x16, 0xb5,
0xc5, 0x6d, 0xa1, 0x2a, 0xad, 0x2c, 0xc5, 0x31,
0x7f, 0xba, 0x7a, 0x92, 0x8e, 0x5e, 0x9d, 0x5f,
0xf8, 0x12, 0x05, 0x23, 0x1b, 0xd1, 0xf6, 0xb7,
0x77, 0xaa, 0xcd, 0x95, 0x72, 0xbc, 0x9e, 0xdf,
0x58, 0x5d, 0x4b, 0x97, 0xae, 0x92, 0x17, 0xb9,
0x44, 0xd0, 0x80, 0xc8, 0xfa, 0x3e, 0xbf, 0xb3,
0xdc, 0x54, 0xcb, 0x07, 0x75, 0x6e, 0xa3, 0xb6,
0x76, 0x59, 0x92, 0x93, 0xa9, 0xdc, 0x51, 0x50,
0x99, 0x6b, 0xcc, 0x35, 0xe6, 0x1a, 0xff, 0x57,
0x23, 0x08, 0x42, 0xcb, 0xe9, 0x1b, 0xd6, 0x78,
0xc2, 0xec, 0xfe, 0xfc, 0x7a, 0xfb, 0x7d, 0x78,
0xd3, 0x84, 0xdf, 0xd4, 0xf2, 0xa4, 0xfb, 0x08,
0x06, 0x00, 0x00,
}
// Generated by vscode-hexdump
var bigTestData = [...]byte{
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xed, 0x54, 0xcf, 0x4f, 0x1a, 0x41,
0x14, 0x7e, 0xc2, 0x02, 0xcb, 0x96, 0x82, 0xb1,
0xc4, 0x10, 0x63, 0xcc, 0xab, 0xb5, 0x84, 0xa5,
0xdb, 0xcd, 0x42, 0x11, 0x89, 0xb1, 0x88, 0x16,
0x2c, 0x9a, 0x0d, 0x1a, 0xd8, 0xa8, 0x31, 0x86,
0xb8, 0x2b, 0xc3, 0x82, 0x2e, 0xbb, 0x66, 0x77,
0xb0, 0xf1, 0xd4, 0x4b, 0x7b, 0x6c, 0x7a, 0xeb,
0x3f, 0xd3, 0x23, 0x7f, 0x43, 0xcf, 0xbd, 0xf6,
0xbf, 0xa0, 0xc3, 0x2f, 0x7b, 0x69, 0xcf, 0xbd,
0xf0, 0x32, 0xc9, 0xf7, 0xe6, 0xbd, 0x6f, 0xe6,
0x7b, 0x6f, 0x26, 0x79, 0x02, 0x04, 0x54, 0x72,
0x4f, 0x2c, 0x0e, 0x78, 0xcb, 0xb1, 0x4d, 0x8d,
0x78, 0xf4, 0xe3, 0x70, 0x62, 0x3e, 0x08, 0x7b,
0x1d, 0xc7, 0xa5, 0x93, 0x18, 0x0f, 0x82, 0x47,
0xdd, 0xee, 0x84, 0x02, 0x62, 0xb5, 0xa2, 0xaa,
0xc7, 0x78, 0x76, 0x5c, 0x57, 0xcb, 0xa8, 0x55,
0x0f, 0x1b, 0xc8, 0xd6, 0x1e, 0x6a, 0x95, 0x86,
0x86, 0x0d, 0xad, 0x7e, 0x58, 0x7b, 0x8f, 0x83,
0xcf, 0x83, 0x4f, 0x83, 0x6f, 0xcf, 0x03, 0x10,
0x6e, 0x5b, 0x8e, 0x3e, 0xbe, 0xa5, 0x38, 0x4c,
0x64, 0xfd, 0x10, 0xea, 0xda, 0x74, 0xa6, 0x23,
0x40, 0xdc, 0x66, 0x2e, 0x69, 0xe1, 0xb5, 0xd3,
0xbb, 0x73, 0xfa, 0x76, 0x0b, 0x29, 0xdb, 0x0b,
0xe0, 0xef, 0xe8, 0x3d, 0x1e, 0x38, 0x5b, 0xef,
0x11, 0x08, 0x56, 0xf5, 0xde, 0x5d, 0xdf, 0x0b,
0x40, 0xe0, 0x5e, 0xb7, 0xfa, 0x64, 0xb7, 0x04,
0x00, 0x8c, 0x41, 0x4c, 0x73, 0xc6, 0x08, 0x55,
0x4c, 0xd3, 0x20, 0x2e, 0x7d, 0xa4, 0xc0, 0xc8,
0xc2, 0x10, 0xb3, 0xba, 0xde, 0x58, 0x0b, 0x53,
0xa3, 0xee, 0x44, 0x8e, 0x45, 0x03, 0x30, 0xb1,
0x27, 0x53, 0x8c, 0x4c, 0xf1, 0xe9, 0x14, 0xa3,
0x53, 0x8c, 0x85, 0xe1, 0xd9, 0x9f, 0xe3, 0xb3,
0xf2, 0x44, 0x81, 0xa5, 0x7c, 0x33, 0xdd, 0xd8,
0xbb, 0xc7, 0xaa, 0x75, 0x13, 0x5f, 0x28, 0x1c,
0x08, 0xd7, 0x2e, 0xd1, 0x59, 0x3f, 0xaf, 0x1d,
0x1b, 0x60, 0x21, 0x59, 0xdf, 0xfa, 0xf1, 0x05,
0xfe, 0xc1, 0xce, 0xfc, 0x9d, 0xbd, 0x00, 0xbc,
0xf1, 0x40, 0xc9, 0xf8, 0x85, 0x42, 0x40, 0x46,
0xfe, 0x9e, 0xeb, 0xea, 0x0f, 0x93, 0x3a, 0x68,
0x87, 0x60, 0xbb, 0xeb, 0x32, 0x37, 0xa3, 0x28,
0x0a, 0x8e, 0xbb, 0xf5, 0xd0, 0x69, 0x63, 0xca,
0x4e, 0xdb, 0xe9, 0xec, 0xe6, 0xe6, 0x2b, 0x3b,
0xbd, 0x25, 0xbe, 0x64, 0x49, 0x09, 0x3d, 0xaa,
0xbb, 0x94, 0xfd, 0x18, 0x7e, 0xe8, 0xd2, 0x0e,
0xda, 0x6f, 0x15, 0x4c, 0xb1, 0x68, 0x3e, 0x2b,
0xe1, 0x9b, 0x9c, 0x84, 0x99, 0xbc, 0x84, 0x05,
0x09, 0x65, 0x59, 0x16, 0x45, 0x00, 0xff, 0x2f,
0x28, 0xae, 0x2f, 0xf2, 0xc2, 0xb2, 0xa4, 0x2e,
0x1d, 0x20, 0x77, 0x5a, 0x3b, 0xb9, 0x8c, 0xca,
0xe7, 0x29, 0xdf, 0x51, 0x41, 0xc9, 0x16, 0xb5,
0xc5, 0x6d, 0xa1, 0x2a, 0xad, 0x2c, 0xc5, 0x31,
0x7f, 0xba, 0x7a, 0x92, 0x8e, 0x5e, 0x9d, 0x5f,
0xf8, 0x12, 0x05, 0x23, 0x1b, 0xd1, 0xf6, 0xb7,
0x77, 0xaa, 0xcd, 0x95, 0x72, 0xbc, 0x9e, 0xdf,
0x58, 0x5d, 0x4b, 0x97, 0xae, 0x92, 0x17, 0xb9,
0x44, 0xd0, 0x80, 0xc8, 0xfa, 0x3e, 0xbf, 0xb3,
0xdc, 0x54, 0xcb, 0x07, 0x75, 0x6e, 0xa3, 0xb6,
0x76, 0x59, 0x92, 0x93, 0xa9, 0xdc, 0x51, 0x50,
0x99, 0x6b, 0xcc, 0x35, 0xe6, 0x1a, 0xff, 0x57,
0x23, 0x08, 0x42, 0xcb, 0xe9, 0x1b, 0xd6, 0x78,
0xc2, 0xec, 0xfe, 0xfc, 0x7a, 0xfb, 0x7d, 0x78,
0xd3, 0x84, 0xdf, 0xd4, 0xf2, 0xa4, 0xfb, 0x08,
0x06, 0x00, 0x00,
}
type BitTestStruct struct {
NCT struct {
Egg struct {
Name string `nbt:"name"`
Value float32 `nbt:"value"`
} `nbt:"egg"`
Ham struct {
Name string `nbt:"name"`
Value float32 `nbt:"value"`
} `nbt:"ham"`
} `nbt:"nested compound test"`
IntTest int `nbt:"intTest"`
ByteTest byte `nbt:"byteTest"`
StringTest string `nbt:"stringTest"`
ListTest []int64 `nbt:"listTest (long)"`
DoubleTest float64 `nbt:"doubleTest"`
LongTest int64 `nbt:"longTest"`
ListTest2 [2]struct {
CreatedOn int64 `nbt:"created-on"`
Name string `nbt:"name"`
} `nbt:"listTest (compound)"`
ByteArrayTest []byte `nbt:"byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))"`
ShortTest int16 `nbt:"shortTest"`
}
type BigTestStruct struct {
LongTest int64 `nbt:"longTest"`
ShortTest int16 `nbt:"shortTest"`
StringTest string `nbt:"stringTest"`
FloatTest float32 `nbt:"floatTest"`
IntTest int32 `nbt:"intTest"`
NCT struct {
Ham struct {
Name string `nbt:"name"`
Value float32 `nbt:"value"`
} `nbt:"ham"`
Egg struct {
Name string `nbt:"name"`
Value float32 `nbt:"value"`
} `nbt:"egg"`
} `nbt:"nested compound test"`
ListTest []int64 `nbt:"listTest (long)" nbt_type:"list"`
ListTest2 [2]struct {
Name string `nbt:"name"`
CreatedOn int64 `nbt:"created-on"`
} `nbt:"listTest (compound)"`
ByteTest byte `nbt:"byteTest"`
ByteArrayTest []byte `nbt:"byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))"`
DoubleTest float64 `nbt:"doubleTest"`
}
//test parse
var value BitTestStruct
r, err := gzip.NewReader(bytes.NewReader(data))
if err != nil {
t.Fatal(err)
}
if err := NewDecoder(r).Decode(&value); err != nil {
t.Fatal(err)
}
var want BitTestStruct
func MakeBigTestStruct() BigTestStruct {
var want BigTestStruct
want.NCT.Egg.Name = "Eggbert"
want.NCT.Egg.Value = 0.5
want.NCT.Ham.Name = "Hampus"
@ -174,6 +166,7 @@ func TestUnmarshal_bitTest(t *testing.T) {
want.StringTest = "HELLO WORLD THIS IS A TEST STRING \xc3\x85\xc3\x84\xc3\x96!"
want.ListTest = []int64{11, 12, 13, 14, 15}
want.DoubleTest = 0.49312871321823148
want.FloatTest = 0.49823147058486938
want.LongTest = 9223372036854775807
want.ListTest2[0].CreatedOn = 1264099775885
want.ListTest2[0].Name = "Compound tag #0"
@ -184,14 +177,28 @@ func TestUnmarshal_bitTest(t *testing.T) {
want.ByteArrayTest[n] = byte((n*n*255 + n*7) % 100)
}
want.ShortTest = 32767
return want
}
func TestUnmarshal_bigTest(t *testing.T) {
//test parse
var value BigTestStruct
r, err := gzip.NewReader(bytes.NewReader(bigTestData[:]))
if err != nil {
t.Fatal(err)
}
if err := NewDecoder(r).Decode(&value); err != nil {
t.Fatal(err)
}
want := MakeBigTestStruct()
if !reflect.DeepEqual(value, want) {
t.Errorf("parse fail, expect %v, get %v", want, value)
}
//test rawRead
var empty struct{}
r, err = gzip.NewReader(bytes.NewReader(data))
r, err = gzip.NewReader(bytes.NewReader(bigTestData[:]))
if err != nil {
t.Fatal(err)
}
@ -199,9 +206,8 @@ func TestUnmarshal_bitTest(t *testing.T) {
t.Fatal(err)
}
//test unmarshal to interface{}
var inf interface{}
r, err = gzip.NewReader(bytes.NewReader(data))
r, err = gzip.NewReader(bytes.NewReader(bigTestData[:]))
if err != nil {
t.Fatal(err)
}
@ -211,6 +217,24 @@ func TestUnmarshal_bitTest(t *testing.T) {
// t.Log(inf)
}
func TestMarshal_bigTest(t *testing.T) {
var b bytes.Buffer
err := MarshalCompound(&b, MakeBigTestStruct(), "Level")
if err != nil {
t.Error(err)
}
rd, _ := gzip.NewReader(bytes.NewReader(bigTestData[:]))
want, err := ioutil.ReadAll(rd)
if err != nil {
t.Error(err)
}
if !bytes.Equal(b.Bytes(), want) {
t.Errorf("got:\n[% 2x]\nwant:\n[% 2x]", b.Bytes(), want)
}
}
func TestUnmarshal_IntArray(t *testing.T) {
data := []byte{
TagIntArray, 0, 0,