diff --git a/tilemapper/blockdecoder.go b/tilemapper/blockdecoder.go new file mode 100644 index 0000000..8a3073b --- /dev/null +++ b/tilemapper/blockdecoder.go @@ -0,0 +1,170 @@ +// 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 +} diff --git a/tilemapper/colors.go b/tilemapper/colors.go index 8132a3f..49a51ee 100644 --- a/tilemapper/colors.go +++ b/tilemapper/colors.go @@ -30,10 +30,10 @@ func ParseColors(filename string) (colors map[string]color.RGBA, err error) { } c := color.RGBA{A: 0xff} var name string - if n, _ := fmt.Sscanf( - line, "%s %d %d %d %d", &name, &c.R, &c.G, &c.B, &c.A); n > 0 { - colors[name] = c - } + if n, _ := fmt.Sscanf( + line, "%s %d %d %d %d", &name, &c.R, &c.G, &c.B, &c.A); n > 0 { + colors[name] = c + } } err = scanner.Err() return diff --git a/tilemapper/main.go b/tilemapper/main.go index 220305e..9279928 100644 --- a/tilemapper/main.go +++ b/tilemapper/main.go @@ -65,9 +65,16 @@ func main() { numBlocks := 0 bytesTotal := int64(0) + versions := make(map[byte]int) + count := func(block *common.Block) { numBlocks++ bytesTotal += int64(len(block.Data)) + if db, err := NewDecodedBlock(block.Data); err == nil { + versions[db.Version]++ + } else { + log.Printf("WARN: Decoding block failed: %s", err) + } } if err = client.QueryCuboid(cuboid, count); err != nil { @@ -76,4 +83,8 @@ func main() { fmt.Printf("num of blocks: %d\n", numBlocks) fmt.Printf("num of bytes: %d\n", bytesTotal) + + for version, count := range versions { + fmt.Printf("%d: %d\n", version, count) + } }