Created
July 10, 2012 12:51
-
-
Save bemasher/3083068 to your computer and use it in GitHub Desktop.
Named Binary Tag parser written in Golang using reflection to populate native types.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is the structure of the binary nbt file | |
TAG_Compound('Level') { | |
TAG_Compound('nested compound test') { | |
TAG_Compound('egg') { | |
TAG_String('name'): 'Eggbert' | |
TAG_Float('value'): 0.5 | |
} | |
TAG_Compound('ham') { | |
TAG_String('name'): 'Hampus' | |
TAG_Float('value'): 0.75 | |
} | |
} | |
TAG_Int('intTest'): 2147483647 | |
TAG_Byte('byteTest'): 127 | |
TAG_String('stringTest'): 'HELLO WORLD THIS IS A TEST STRING \xc3\x85\xc3\x84\xc3\x96!' | |
TAG_List('listTest (long)') { | |
TAG_Long(None): 11 | |
TAG_Long(None): 12 | |
TAG_Long(None): 13 | |
TAG_Long(None): 14 | |
TAG_Long(None): 15 | |
} | |
TAG_Double('doubleTest'): 0.49312871321823148 | |
TAG_Float('floatTest'): 0.49823147058486938 | |
TAG_Long('longTest'): 9223372036854775807L | |
TAG_List('listTest (compound)') { | |
TAG_Compound(None) { | |
TAG_Long('created-on'): 1264099775885L | |
TAG_String('name'): 'Compound tag #0' | |
} | |
TAG_Compound(None) { | |
TAG_Long('created-on'): 1264099775885L | |
TAG_String('name'): 'Compound tag #1' | |
} | |
} | |
TAG_Byte_Array('byteArrayTest') | |
TAG_Short('shortTest'): 32767 | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"io" | |
"os" | |
"fmt" | |
"math" | |
"reflect" | |
"strings" | |
"errhandler" | |
"compress/gzip" | |
"encoding/json" | |
"encoding/binary" | |
) | |
const ( | |
NBTFILE = "bigtest.nbt" | |
) | |
const ( | |
TAG_END = iota | |
TAG_BYTE | |
TAG_SHORT | |
TAG_INT | |
TAG_LONG | |
TAG_FLOAT | |
TAG_DOUBLE | |
TAG_BYTE_ARRAY | |
TAG_STRING | |
TAG_LIST | |
TAG_COMPOUND | |
TAG_INT_ARRAY | |
TAG_UNKNOWN | |
) | |
type TagType byte | |
func (t TagType) String() string { | |
switch t { | |
case TAG_END: return "TAG_END" | |
case TAG_BYTE: return "TAG_BYTE" | |
case TAG_SHORT: return "TAG_SHORT" | |
case TAG_INT: return "TAG_INT" | |
case TAG_LONG: return "TAG_LONG" | |
case TAG_FLOAT: return "TAG_FLOAT" | |
case TAG_DOUBLE: return "TAG_DOUBLE" | |
case TAG_BYTE_ARRAY: return "TAG_BYTE_ARRAY" | |
case TAG_STRING: return "TAG_STRING" | |
case TAG_LIST: return "TAG_LIST" | |
case TAG_COMPOUND: return "TAG_COMPOUND" | |
case TAG_INT_ARRAY: return "TAG_INT_ARRAY" | |
} | |
return "TAG_UNKNOWN" | |
} | |
func StructFields(d reflect.Value) map[string]reflect.Value { | |
if d.Kind() == reflect.Struct { | |
tags := make(map[string]reflect.Value, 0) | |
tags[strings.ToLower(d.Type().Name())] = d | |
for i := 0; i < d.NumField(); i++ { | |
if field := d.Type().Field(i); field.Tag.Get("nbt") == "" { | |
tags[strings.ToLower(field.Name)] = d.Field(i) | |
} else { | |
tags[strings.ToLower(field.Tag.Get("nbt"))] = d.Field(i) | |
} | |
} | |
return tags | |
} | |
return nil | |
} | |
func Read(r io.Reader, data interface{}) { | |
ReadTag(r, TAG_UNKNOWN, reflect.ValueOf(data).Elem()) | |
} | |
func ReadTag(r io.Reader, tagType TagType, data reflect.Value) (name string, newTagType TagType) { | |
if tagType == TAG_UNKNOWN { | |
newTagType = TagType(ReadByte(r)) | |
if newTagType == TAG_END { | |
return | |
} | |
name = ReadString(r) | |
} else { | |
newTagType = TagType(tagType) | |
} | |
fields := StructFields(data) | |
var field reflect.Value | |
if data.IsValid() { | |
field = fields[strings.ToLower(name)] | |
} | |
if name == "" { | |
field = data | |
} | |
var temp interface{} | |
var list reflect.Value | |
switch newTagType { | |
case TAG_BYTE: temp = ReadByte(r) | |
case TAG_SHORT: temp = ReadShort(r) | |
case TAG_INT: temp = ReadInt(r) | |
case TAG_LONG: temp = ReadLong(r) | |
case TAG_FLOAT: temp = ReadFloat(r) | |
case TAG_DOUBLE: temp = ReadDouble(r) | |
case TAG_BYTE_ARRAY: temp = ReadByteArray(r) | |
case TAG_STRING: temp = ReadString(r) | |
case TAG_INT_ARRAY: temp = ReadIntArray(r) | |
case TAG_COMPOUND: ReadCompound(r, field) | |
case TAG_LIST: list = ReadList(r, field) | |
} | |
if field.IsValid() { | |
if temp != nil { | |
field.Set(reflect.ValueOf(temp)) | |
} else if list.IsValid() { | |
field.Set(list) | |
} | |
} | |
return | |
} | |
func ReadByte(r io.Reader) (i byte) { | |
b := make([]byte, 1) | |
_, err := r.Read(b) | |
if err != io.EOF { | |
errhandler.Handle("Error reading Byte: ", err) | |
} | |
i = b[0] | |
return | |
} | |
func ReadShort(r io.Reader) (i int16) { | |
err := binary.Read(r, binary.BigEndian, &i) | |
errhandler.Handle("Error reading int16: ", err) | |
return | |
} | |
func ReadInt(r io.Reader) (i int32) { | |
err := binary.Read(r, binary.BigEndian, &i) | |
errhandler.Handle("Error reading int32: ", err) | |
return | |
} | |
func ReadLong(r io.Reader) (i int64) { | |
err := binary.Read(r, binary.BigEndian, &i) | |
errhandler.Handle("Error reading int32: ", err) | |
return | |
} | |
func ReadFloat(r io.Reader) (i float32) { | |
b := make([]byte, 4) | |
_, err := r.Read(b) | |
errhandler.Handle("Error reading Float: ", err) | |
i = math.Float32frombits(binary.BigEndian.Uint32(b)) | |
return | |
} | |
func ReadDouble(r io.Reader) (i float64) { | |
b := make([]byte, 8) | |
_, err := r.Read(b) | |
errhandler.Handle("Error reading Double: ", err) | |
i = math.Float64frombits(binary.BigEndian.Uint64(b)) | |
return | |
} | |
func ReadByteArray(r io.Reader) (i []byte) { | |
i = make([]byte, ReadInt(r)) | |
_, err := r.Read(i) | |
errhandler.Handle("Error reading Byte Array: ", err) | |
return | |
} | |
func ReadString(r io.Reader) string { | |
result := make([]byte, ReadShort(r)) | |
_, err := r.Read(result) | |
errhandler.Handle("Error reading String: ", err) | |
return string(result) | |
} | |
func ReadIntArray(r io.Reader) (list []int32) { | |
length := int(ReadInt(r)) | |
for i := 0; i < length; i++ { | |
list = append(list, ReadInt(r)) | |
} | |
return | |
} | |
func ReadCompound(r io.Reader, data reflect.Value) { | |
for _, tagType := ReadTag(r, TAG_UNKNOWN, data); tagType != TAG_END; _, tagType = ReadTag(r, TAG_UNKNOWN, data) {} | |
} | |
func ReadList(r io.Reader, data reflect.Value) reflect.Value { | |
listType := TagType(ReadByte(r)) | |
length := ReadInt(r) | |
for i := int32(0); i < length; i++ { | |
if data.IsValid() { | |
temp := reflect.Indirect(reflect.New(data.Type().Elem())) | |
ReadTag(r, listType, temp) | |
data = reflect.Append(data, temp) | |
} | |
} | |
return data | |
} | |
type Level struct { | |
Nested Nested `nbt:"nested compound test"` | |
ByteTest byte | |
IntTest int32 | |
StringTest string | |
ListTestLong []int64 `nbt:"listTest (long)"` | |
DoubleTest float64 | |
FloatTest float32 | |
LongTest int64 | |
ListTestCompound []ListTest `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 | |
} | |
type Nested struct { | |
Egg Egg | |
Ham Ham | |
} | |
type Egg struct { | |
Name string | |
Value float32 | |
} | |
type Ham struct { | |
Name string | |
Value float32 | |
} | |
type ListTest struct { | |
CreatedOn int64 `nbt:"created-on"` | |
Name string | |
} | |
func main() { | |
nbtFile, err := os.Open(NBTFILE) | |
errhandler.Handle("Error opening chunk file: ", err) | |
defer nbtFile.Close() | |
rawNbtFile, err := gzip.NewReader(nbtFile) | |
errhandler.Handle("Error opening chunk file: ", err) | |
var l Level | |
Read(rawNbtFile, &l) | |
// Use json for pretty printing | |
data, err := json.MarshalIndent(l, "", "\t") | |
errhandler.Handle("Error opening chunk file: ", err) | |
fmt.Printf("%s\n", data) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"Nested": { | |
"Egg": { | |
"Name": "Eggbert", | |
"Value": 0.5 | |
}, | |
"Ham": { | |
"Name": "Hampus", | |
"Value": 0.75 | |
} | |
}, | |
"ByteTest": 127, | |
"IntTest": 2147483647, | |
"StringTest": "HELLO WORLD THIS IS A TEST STRING ÅÄÖ!", | |
"ListTestLong": [ | |
11, | |
12, | |
13, | |
14, | |
15 | |
], | |
"DoubleTest": 0.4931287132182315, | |
"FloatTest": 0.49823147, | |
"LongTest": 9223372036854775807, | |
"ListTestCompound": [ | |
{ | |
"CreatedOn": 1264099775885, | |
"Name": "Compound tag #0" | |
}, | |
{ | |
"CreatedOn": 1264099775885, | |
"Name": "Compound tag #1" | |
} | |
], | |
"ByteArrayTest": "AD4iEAgKFixMEkYgBFZOUFwOLlgoAko4MDI+VBA6CkgsGhIUIDZWHFAqDmBYWgIYOGIyDFRCOjxIXhpEFFI2JBweKkBgJlo0GAZiAAwiQgg8Fl5MREZSBCROHlxALiYoNEoGMAA+IhAIChYsTBJGIARWTlBcDi5YKAJKODAyPlQQOgpILBoSFCA2VhxQKg5gWFoCGDhiMgxUQjo8SF4aRBRSNiQcHipAYCZaNBgGYgAMIkIIPBZeTERGUgQkTh5cQC4mKDRKBjAAPiIQCAoWLEwSRiAEVk5QXA4uWCgCSjgwMj5UEDoKSCwaEhQgNlYcUCoOYFhaAhg4YjIMVEI6PEheGkQUUjYkHB4qQGAmWjQYBmIADCJCCDwWXkxERlIEJE4eXEAuJig0SgYwAD4iEAgKFixMEkYgBFZOUFwOLlgoAko4MDI+VBA6CkgsGhIUIDZWHFAqDmBYWgIYOGIyDFRCOjxIXhpEFFI2JBweKkBgJlo0GAZiAAwiQgg8Fl5MREZSBCROHlxALiYoNEoGMAA+IhAIChYsTBJGIARWTlBcDi5YKAJKODAyPlQQOgpILBoSFCA2VhxQKg5gWFoCGDhiMgxUQjo8SF4aRBRSNiQcHipAYCZaNBgGYgAMIkIIPBZeTERGUgQkTh5cQC4mKDRKBjAAPiIQCAoWLEwSRiAEVk5QXA4uWCgCSjgwMj5UEDoKSCwaEhQgNlYcUCoOYFhaAhg4YjIMVEI6PEheGkQUUjYkHB4qQGAmWjQYBmIADCJCCDwWXkxERlIEJE4eXEAuJig0SgYwAD4iEAgKFixMEkYgBFZOUFwOLlgoAko4MDI+VBA6CkgsGhIUIDZWHFAqDmBYWgIYOGIyDFRCOjxIXhpEFFI2JBweKkBgJlo0GAZiAAwiQgg8Fl5MREZSBCROHlxALiYoNEoGMAA+IhAIChYsTBJGIARWTlBcDi5YKAJKODAyPlQQOgpILBoSFCA2VhxQKg5gWFoCGDhiMgxUQjo8SF4aRBRSNiQcHipAYCZaNBgGYgAMIkIIPBZeTERGUgQkTh5cQC4mKDRKBjAAPiIQCAoWLEwSRiAEVk5QXA4uWCgCSjgwMj5UEDoKSCwaEhQgNlYcUCoOYFhaAhg4YjIMVEI6PEheGkQUUjYkHB4qQGAmWjQYBmIADCJCCDwWXkxERlIEJE4eXEAuJig0SgYwAD4iEAgKFixMEkYgBFZOUFwOLlgoAko4MDI+VBA6CkgsGhIUIDZWHFAqDmBYWgIYOGIyDFRCOjxIXhpEFFI2JBweKkBgJlo0GAZiAAwiQgg8Fl5MREZSBCROHlxALiYoNEoGMA==", | |
"ShortTest": 32767 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment