// Copyright 2014 by Sascha L. Teichmann // Use of this source code is governed by the MIT license // that can be found in the LICENSE file. package main import ( "compress/zlib" "encoding/binary" "errors" "io" "io/ioutil" ) var ( ErrMapContentSizeMismatch = errors.New("Content size does not match.") ) const ( mapBlockSize = 16 nodeCount = mapBlockSize * mapBlockSize * mapBlockSize ) type DecodedBlock struct { Version byte MapContent []byte AirId int IgnoreId int NameMap map[int]string } type PosBuf struct { Data []byte Pos int } func NewDecodedBlock(data []byte) (db *DecodedBlock, err error) { version := data[0] contentWidth := int(data[2]) paramsWidth := int(data[3]) uncompressedLen := nodeCount * (contentWidth + paramsWidth) offset := 2 if version >= 22 { offset = 4 } buf := NewPosBuf(data[offset:]) var zr io.ReadCloser if zr, err = zlib.NewReader(buf); err != nil { return } var mapContent []byte if mapContent, err = ioutil.ReadAll(zr); err != nil { return } zr.Close() if uncompressedLen != len(mapContent) { err = ErrMapContentSizeMismatch return } offset += buf.Pos buf.Pos = 0 buf.Data = data[offset:] if zr, err = zlib.NewReader(buf); err != nil { return } // Discard the meta data. _, err = io.Copy(ioutil.Discard, zr) zr.Close() if err != nil { return } offset += buf.Pos switch { case version <= 21: offset += 2 case version == 23: offset++ case version == 24: ver := data[offset] offset++ if ver == 1 { num := int(binary.BigEndian.Uint16(data[offset:])) offset += 2 + 10*num } } offset++ numStaticObjects := int(binary.BigEndian.Uint16(data[offset:])) offset += 2 for i := 0; i < numStaticObjects; i++ { offset += 13 dataSize := int(binary.BigEndian.Uint16(data[offset:])) offset += dataSize + 2 } offset += 4 airId, ignoreId := -1, -1 nameMap := make(map[int]string) if version >= 22 { offset++ numMappings := int(binary.BigEndian.Uint16(data[offset:])) offset += 2 for i := 0; i < numMappings; i++ { nodeId := int(binary.BigEndian.Uint16(data[offset:])) offset += 2 nameLen := int(binary.BigEndian.Uint16(data[offset:])) offset += 2 name := string(data[offset : offset+nameLen]) offset += nameLen switch name { case "air": airId = nodeId case "ignore": ignoreId = nodeId default: nameMap[nodeId] = name } } } db = &DecodedBlock{ Version: version, MapContent: mapContent, AirId: airId, IgnoreId: ignoreId, NameMap: nameMap} return } func NewPosBuf(data []byte) *PosBuf { return &PosBuf{Data: data} } func (pb *PosBuf) Read(p []byte) (int, error) { pl := len(p) ml := len(pb.Data) if pb.Pos >= ml { return 0, io.EOF } rest := ml - pb.Pos if pl > rest { copy(p, pb.Data[pb.Pos:]) pb.Pos = ml return rest, io.EOF } copy(p, pb.Data[pb.Pos:pb.Pos+pl]) pb.Pos += pl return pl, nil } func (pb *PosBuf) ReadByte() (byte, error) { if pb.Pos >= len(pb.Data) { return 0, io.EOF } c := pb.Data[pb.Pos] pb.Pos++ return c, nil }