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 # 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 # Docs
[![GoDoc](https://godoc.org/github.com/Tnze/go-mc/nbt?status.svg)](https://godoc.org/github.com/Tnze/go-mc/nbt) [![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" "io"
"math" "math"
"reflect" "reflect"
"strings"
)
var (
ErrMustBeStruct = errors.New("a compound can only be a struct")
) )
func Marshal(w io.Writer, v interface{}) error { func Marshal(w io.Writer, v interface{}) error {
return NewEncoder(w).Encode(v) 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 { type Encoder struct {
w io.Writer w io.Writer
} }
@ -21,166 +35,87 @@ func NewEncoder(w io.Writer) *Encoder {
func (e *Encoder) Encode(v interface{}) error { func (e *Encoder) Encode(v interface{}) error {
val := reflect.ValueOf(v) 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 { func (e *Encoder) marshal(val reflect.Value, tagType byte, tagName string) error {
switch vk := val.Kind(); vk { if err := e.writeHeader(val, tagType, tagName); err != nil {
default: return err
return errors.New("unknown type " + vk.String()) }
return e.writeValue(val, tagType)
}
case reflect.Uint8: func (e *Encoder) writeHeader(val reflect.Value, tagType byte, tagName string) (err error) {
if err := e.writeTag(TagByte, tagName); err != nil { if tagType == TagList {
return err 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())}) _, err := e.w.Write([]byte{byte(val.Uint())})
return err return err
case TagShort:
case reflect.Int16, reflect.Uint16:
if err := e.writeTag(TagShort, tagName); err != nil {
return err
}
return e.writeInt16(int16(val.Int())) return e.writeInt16(int16(val.Int()))
case TagInt:
case reflect.Int32, reflect.Uint32:
if err := e.writeTag(TagInt, tagName); err != nil {
return err
}
return e.writeInt32(int32(val.Int())) return e.writeInt32(int32(val.Int()))
case TagFloat:
case reflect.Float32:
if err := e.writeTag(TagFloat, tagName); err != nil {
return err
}
return e.writeInt32(int32(math.Float32bits(float32(val.Float())))) return e.writeInt32(int32(math.Float32bits(float32(val.Float()))))
case TagLong:
case reflect.Int64, reflect.Uint64: return e.writeInt64(val.Int())
if err := e.writeTag(TagLong, tagName); err != nil { case TagDouble:
return err
}
return e.writeInt64(int64(val.Int()))
case reflect.Float64:
if err := e.writeTag(TagDouble, tagName); err != nil {
return err
}
return e.writeInt64(int64(math.Float64bits(val.Float()))) return e.writeInt64(int64(math.Float64bits(val.Float())))
case TagByteArray, TagIntArray, TagLongArray:
case reflect.Array, reflect.Slice:
n := val.Len() n := val.Len()
switch val.Type().Elem().Kind() { if err := e.writeInt32(int32(n)); err != nil {
case reflect.Uint8: // []byte return err
if err := e.writeTag(TagByteArray, tagName); err != nil { }
return err
} if tagType == TagByteArray {
if err := e.writeInt32(int32(val.Len())); err != nil {
return err
}
_, err := e.w.Write(val.Bytes()) _, err := e.w.Write(val.Bytes())
return err return err
} else {
for i := 0; i < n; i++ {
v := val.Index(i).Int()
case reflect.Int32: var err error
if err := e.writeTag(TagIntArray, tagName); err != nil { if tagType == TagIntArray {
return err err = e.writeInt32(int32(v))
} } else if tagType == TagLongArray {
if err := e.writeInt32(int32(n)); err != nil { err = e.writeInt64(v)
return err
}
for i := 0; i < n; i++ {
if err := e.writeInt32(int32(val.Index(i).Int())); err != nil {
return err
} }
}
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 { if err != nil {
return err return err
} }
} }
default:
return errors.New("unknown type " + val.Type().String() + " slice")
} }
case reflect.String: case TagList:
if err := e.writeTag(TagString, tagName); err != nil { for i := 0; i < val.Len(); i++ {
return err 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 { if err := e.writeInt16(int16(val.Len())); err != nil {
return err return err
} }
_, err := e.w.Write([]byte(val.String())) _, err := e.w.Write([]byte(val.String()))
return err return err
case reflect.Struct: case TagCompound:
if err := e.writeTag(TagCompound, ""); err != nil { if val.Kind() == reflect.Interface {
return err val = reflect.ValueOf(val.Interface())
} }
n := val.NumField() n := val.NumField()
@ -191,12 +126,8 @@ func (e *Encoder) marshal(val reflect.Value, tagName string) error {
continue // Private field continue // Private field
} }
tagName := f.Name tagProps := parseTag(f, tag)
if tag != "" { err := e.marshal(val.Field(i), tagProps.Type, tagProps.Name)
tagName = tag
}
err := e.marshal(val.Field(i), tagName)
if err != nil { if err != nil {
return err return err
} }
@ -207,6 +138,65 @@ func (e *Encoder) marshal(val reflect.Value, tagName string) error {
return nil 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 { func (e *Encoder) writeTag(tagType byte, tagName string) error {
if _, err := e.w.Write([]byte{tagType}); err != nil { if _, err := e.w.Write([]byte{tagType}); err != nil {
return err return err
@ -233,11 +223,6 @@ func (e *Encoder) writeListHeader(elementType byte, tagName string, n int) (err
return nil 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 { func (e *Encoder) writeInt16(n int16) error {
_, err := e.w.Write([]byte{byte(n >> 8), byte(n)}) _, err := e.w.Write([]byte{byte(n >> 8), byte(n)})
return err 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) { func TestMarshal_InterfaceArray(t *testing.T) {
type Struct1 struct { type Struct1 struct {
Val int32 Val int32
@ -76,16 +89,15 @@ func TestMarshal_InterfaceArray(t *testing.T) {
want: []byte{ want: []byte{
TagList, 0x00, 0x00 /*no name*/, TagCompound, 0, 0, 0, 2, TagList, 0x00, 0x00 /*no name*/, TagCompound, 0, 0, 0, 2,
// 1st element // 1st element
TagCompound, 0x00, 0x00, /*no name*/
TagInt, 0x00, 0x03, 'V', 'a', 'l', 0x00, 0x00, 0x00, 0x03, // 3 TagInt, 0x00, 0x03, 'V', 'a', 'l', 0x00, 0x00, 0x00, 0x03, // 3
TagEnd, TagEnd,
// 2nd element // 2nd element
TagCompound, 0x00, 0x00, /*no name*/
TagFloat, 0x00, 0x03, 'V', 'a', 'l', 0x3e, 0x99, 0x99, 0x9a, // 0.3 TagFloat, 0x00, 0x03, 'V', 'a', 'l', 0x3e, 0x99, 0x99, 0x9a, // 0.3
TagEnd, TagEnd,
}, },
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
w := &bytes.Buffer{} w := &bytes.Buffer{}
@ -105,24 +117,39 @@ func TestMarshal_StructArray(t *testing.T) {
Val int32 Val int32
} }
type Struct2 struct {
T int32
Ele Struct1
}
type StructCont struct {
V []Struct2
}
tests := []struct { tests := []struct {
name string name string
args []Struct1 args StructCont
want []byte want []byte
}{ }{
{ {
name: "One element struct array", name: "One element struct array",
args: []Struct1{{3}, {-10}}, args: StructCont{[]Struct2{{3, Struct1{3}}, {-10, Struct1{-10}}}},
want: []byte{ want: []byte{
TagList, 0x00, 0x00 /*no name*/, TagCompound, 0, 0, 0, 2, TagCompound, 0x00, 0x00,
// 1st element TagList, 0x00, 0x01, 'V', TagCompound, 0, 0, 0, 2,
TagCompound, 0x00, 0x00, /*no name*/ // 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 TagInt, 0x00, 0x03, 'V', 'a', 'l', 0x00, 0x00, 0x00, 0x03, // 3
TagEnd, TagEnd,
TagEnd,
// 2nd element // 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 TagInt, 0x00, 0x03, 'V', 'a', 'l', 0xff, 0xff, 0xff, 0xf6, // -10
TagEnd, TagEnd,
TagEnd,
TagEnd,
}, },
}, },
} }

View File

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

View File

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