429 lines
9.7 KiB
Go
429 lines
9.7 KiB
Go
package nbt
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"reflect"
|
|
)
|
|
|
|
func Unmarshal(data []byte, v interface{}) error {
|
|
return NewDecoder(bytes.NewReader(data)).Decode(v)
|
|
}
|
|
|
|
func (d *Decoder) Decode(v interface{}) error {
|
|
val := reflect.ValueOf(v)
|
|
if val.Kind() != reflect.Ptr {
|
|
return errors.New("non-pointer passed to Unmarshal")
|
|
}
|
|
tagType, tagName, err := d.readTag()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return d.unmarshal(val.Elem(), tagType, tagName)
|
|
}
|
|
|
|
// ErrEND error will be returned when reading a NBT with only Tag_End
|
|
var ErrEND = errors.New("unexpected TAG_End")
|
|
|
|
func (d *Decoder) unmarshal(val reflect.Value, tagType byte, tagName string) error {
|
|
switch tagType {
|
|
default:
|
|
return fmt.Errorf("unknown Tag 0x%02x", tagType)
|
|
case TagEnd:
|
|
return ErrEND
|
|
|
|
case TagByte:
|
|
value, err := d.r.ReadByte()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch vk := val.Kind(); vk {
|
|
default:
|
|
return errors.New("cannot parse TagByte as " + vk.String())
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
val.SetInt(int64(value))
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
val.SetUint(uint64(value))
|
|
case reflect.Interface:
|
|
val.Set(reflect.ValueOf(value))
|
|
}
|
|
|
|
case TagShort:
|
|
value, err := d.readInt16()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch vk := val.Kind(); vk {
|
|
default:
|
|
return errors.New("cannot parse TagShort as " + vk.String())
|
|
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
val.SetInt(int64(value))
|
|
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
val.SetUint(uint64(value))
|
|
case reflect.Interface:
|
|
val.Set(reflect.ValueOf(value))
|
|
}
|
|
|
|
case TagInt:
|
|
value, err := d.readInt32()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch vk := val.Kind(); vk {
|
|
default:
|
|
return errors.New("cannot parse TagInt as " + vk.String())
|
|
case reflect.Int, reflect.Int32, reflect.Int64:
|
|
val.SetInt(int64(value))
|
|
case reflect.Uint, reflect.Uint32, reflect.Uint64:
|
|
val.SetUint(uint64(value))
|
|
case reflect.Interface:
|
|
val.Set(reflect.ValueOf(value))
|
|
}
|
|
|
|
case TagFloat:
|
|
vInt, err := d.readInt32()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
value := math.Float32frombits(uint32(vInt))
|
|
switch vk := val.Kind(); vk {
|
|
default:
|
|
return errors.New("cannot parse TagFloat as " + vk.String())
|
|
case reflect.Float32:
|
|
val.Set(reflect.ValueOf(value))
|
|
case reflect.Float64:
|
|
val.Set(reflect.ValueOf(float64(value)))
|
|
case reflect.Interface:
|
|
val.Set(reflect.ValueOf(value))
|
|
}
|
|
|
|
case TagLong:
|
|
value, err := d.readInt64()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch vk := val.Kind(); vk {
|
|
default:
|
|
return errors.New("cannot parse TagLong as " + vk.String())
|
|
case reflect.Int, reflect.Int64:
|
|
val.SetInt(int64(value))
|
|
case reflect.Uint, reflect.Uint64:
|
|
val.SetUint(uint64(value))
|
|
case reflect.Interface:
|
|
val.Set(reflect.ValueOf(value))
|
|
}
|
|
|
|
case TagDouble:
|
|
vInt, err := d.readInt64()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
value := math.Float64frombits(uint64(vInt))
|
|
|
|
switch vk := val.Kind(); vk {
|
|
default:
|
|
return errors.New("cannot parse TagDouble as " + vk.String())
|
|
case reflect.Float64:
|
|
val.Set(reflect.ValueOf(value))
|
|
case reflect.Interface:
|
|
val.Set(reflect.ValueOf(value))
|
|
}
|
|
|
|
case TagString:
|
|
s, err := d.readString()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch vk := val.Kind(); vk {
|
|
default:
|
|
return errors.New("cannot parse TagString as " + vk.String())
|
|
case reflect.String:
|
|
val.SetString(s)
|
|
case reflect.Interface:
|
|
val.Set(reflect.ValueOf(s))
|
|
}
|
|
|
|
case TagByteArray:
|
|
var ba []byte
|
|
aryLen, err := d.readInt32()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if ba, err = d.readNByte(int(aryLen)); err != nil {
|
|
return err
|
|
}
|
|
|
|
switch vt := val.Type(); {
|
|
default:
|
|
return errors.New("cannot parse TagByteArray to " + vt.String() + ", use []byte in this instance")
|
|
case vt == reflect.TypeOf(ba):
|
|
val.SetBytes(ba)
|
|
case vt.Kind() == reflect.Interface:
|
|
val.Set(reflect.ValueOf(ba))
|
|
}
|
|
|
|
case TagIntArray:
|
|
aryLen, err := d.readInt32()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
vt := val.Type() //receiver must be []int or []int32
|
|
if vt.Kind() == reflect.Interface {
|
|
vt = reflect.TypeOf([]int32{}) // pass
|
|
} else if vt.Kind() != reflect.Slice {
|
|
return errors.New("cannot parse TagIntArray to " + vt.String() + ", it must be a slice")
|
|
} else if tk := val.Type().Elem().Kind(); tk != reflect.Int && tk != reflect.Int32 {
|
|
return errors.New("cannot parse TagIntArray to " + vt.String())
|
|
}
|
|
|
|
buf := reflect.MakeSlice(vt, int(aryLen), int(aryLen))
|
|
for i := 0; i < int(aryLen); i++ {
|
|
value, err := d.readInt32()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
buf.Index(i).SetInt(int64(value))
|
|
}
|
|
val.Set(buf)
|
|
|
|
case TagLongArray:
|
|
aryLen, err := d.readInt32()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
vt := val.Type() //receiver must be []int or []int64
|
|
if vt.Kind() == reflect.Interface {
|
|
vt = reflect.TypeOf([]int64{}) // pass
|
|
} else if vt.Kind() != reflect.Slice {
|
|
return errors.New("cannot parse TagIntArray to " + vt.String() + ", it must be a slice")
|
|
} else if val.Type().Elem().Kind() != reflect.Int64 {
|
|
return errors.New("cannot parse TagIntArray to " + vt.String())
|
|
}
|
|
|
|
buf := reflect.MakeSlice(vt, int(aryLen), int(aryLen))
|
|
for i := 0; i < int(aryLen); i++ {
|
|
value, err := d.readInt64()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
buf.Index(i).SetInt(value)
|
|
}
|
|
val.Set(buf)
|
|
|
|
case TagList:
|
|
listType, err := d.r.ReadByte()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
listLen, err := d.readInt32()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// If we need parse TAG_List into slice, make a new with right length.
|
|
// Otherwise if we need parse into array, we check if len(array) are enough.
|
|
var buf reflect.Value
|
|
vk := val.Kind()
|
|
switch vk {
|
|
default:
|
|
return errors.New("cannot parse TagList as " + vk.String())
|
|
case reflect.Interface:
|
|
buf = reflect.ValueOf(make([]interface{}, listLen))
|
|
case reflect.Slice:
|
|
buf = reflect.MakeSlice(val.Type(), int(listLen), int(listLen))
|
|
case reflect.Array:
|
|
if vl := val.Len(); vl < int(listLen) {
|
|
return fmt.Errorf(
|
|
"TagList %s has len %d, but array %v only has len %d",
|
|
tagName, listLen, val.Type(), vl)
|
|
}
|
|
buf = val
|
|
}
|
|
for i := 0; i < int(listLen); i++ {
|
|
if err := d.unmarshal(buf.Index(i), listType, ""); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if vk != reflect.Array {
|
|
val.Set(buf)
|
|
}
|
|
|
|
case TagCompound:
|
|
switch vk := val.Kind(); vk {
|
|
default:
|
|
return errors.New("cannot parse TagCompound as " + vk.String())
|
|
case reflect.Struct:
|
|
tinfo := getTypeInfo(val.Type())
|
|
for {
|
|
tt, tn, err := d.readTag()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tt == TagEnd {
|
|
break
|
|
}
|
|
field := tinfo.findIndexByName(tn)
|
|
if field != -1 {
|
|
err = d.unmarshal(val.Field(field), tt, tn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if err := d.skip(tt); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
case reflect.Interface:
|
|
buf := make(map[string]interface{})
|
|
for {
|
|
tt, tn, err := d.readTag()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tt == TagEnd {
|
|
break
|
|
}
|
|
var value interface{}
|
|
if err = d.unmarshal(reflect.ValueOf(&value).Elem(), tt, tn); err != nil {
|
|
return err
|
|
}
|
|
buf[tn] = value
|
|
}
|
|
val.Set(reflect.ValueOf(buf))
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Decoder) skip(tagType byte) error {
|
|
switch tagType {
|
|
default:
|
|
return fmt.Errorf("unknown to skip 0x%02x", tagType)
|
|
case TagByte:
|
|
_, err := d.r.ReadByte()
|
|
return err
|
|
case TagString:
|
|
_, err := d.readString()
|
|
return err
|
|
case TagShort:
|
|
_, err := d.readNByte(2)
|
|
return err
|
|
case TagInt, TagFloat:
|
|
_, err := d.readNByte(4)
|
|
return err
|
|
case TagLong, TagDouble:
|
|
_, err := d.readNByte(8)
|
|
return err
|
|
case TagByteArray:
|
|
aryLen, err := d.readInt32()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err = d.readNByte(int(aryLen)); err != nil {
|
|
return err
|
|
}
|
|
case TagIntArray:
|
|
aryLen, err := d.readInt32()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i := 0; i < int(aryLen); i++ {
|
|
if _, err := d.readInt32(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
case TagLongArray:
|
|
aryLen, err := d.readInt32()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i := 0; i < int(aryLen); i++ {
|
|
if _, err := d.readInt64(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
case TagList:
|
|
listType, err := d.r.ReadByte()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
listLen, err := d.readInt32()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i := 0; i < int(listLen); i++ {
|
|
if err := d.skip(listType); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case TagCompound:
|
|
for {
|
|
tt, _, err := d.readTag()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tt == TagEnd {
|
|
break
|
|
}
|
|
err = d.skip(tt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *Decoder) readTag() (tagType byte, tagName string, err error) {
|
|
tagType, err = d.r.ReadByte()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if tagType != TagEnd { //Read Tag
|
|
tagName, err = d.readString()
|
|
}
|
|
return
|
|
}
|
|
|
|
func (d *Decoder) readNByte(n int) (buf []byte, err error) {
|
|
buf = make([]byte, n)
|
|
_, err = d.r.Read(buf) //what happened if (returned n) != (argument n) ?
|
|
return
|
|
}
|
|
|
|
func (d *Decoder) readInt16() (int16, error) {
|
|
data, err := d.readNByte(2)
|
|
return int16(data[0])<<8 | int16(data[1]), err
|
|
}
|
|
|
|
func (d *Decoder) readInt32() (int32, error) {
|
|
data, err := d.readNByte(4)
|
|
return int32(data[0])<<24 | int32(data[1])<<16 |
|
|
int32(data[2])<<8 | int32(data[3]), err
|
|
}
|
|
|
|
func (d *Decoder) readInt64() (int64, error) {
|
|
data, err := d.readNByte(8)
|
|
return int64(data[0])<<56 | int64(data[1])<<48 |
|
|
int64(data[2])<<40 | int64(data[3])<<32 |
|
|
int64(data[4])<<24 | int64(data[5])<<16 |
|
|
int64(data[6])<<8 | int64(data[7]), err
|
|
}
|
|
|
|
func (d *Decoder) readString() (string, error) {
|
|
length, err := d.readInt16()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
buf, err := d.readNByte(int(length))
|
|
return string(buf), err
|
|
}
|