Added simple block decoder for tilemapper.

This commit is contained in:
Sascha L. Teichmann 2014-09-09 17:48:39 +02:00
parent b04bb20fe4
commit 73b6fa4fb9
3 changed files with 185 additions and 4 deletions

170
tilemapper/blockdecoder.go Normal file
View File

@ -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
}

View File

@ -30,10 +30,10 @@ func ParseColors(filename string) (colors map[string]color.RGBA, err error) {
} }
c := color.RGBA{A: 0xff} c := color.RGBA{A: 0xff}
var name string var name string
if n, _ := fmt.Sscanf( if n, _ := fmt.Sscanf(
line, "%s %d %d %d %d", &name, &c.R, &c.G, &c.B, &c.A); n > 0 { line, "%s %d %d %d %d", &name, &c.R, &c.G, &c.B, &c.A); n > 0 {
colors[name] = c colors[name] = c
} }
} }
err = scanner.Err() err = scanner.Err()
return return

View File

@ -65,9 +65,16 @@ func main() {
numBlocks := 0 numBlocks := 0
bytesTotal := int64(0) bytesTotal := int64(0)
versions := make(map[byte]int)
count := func(block *common.Block) { count := func(block *common.Block) {
numBlocks++ numBlocks++
bytesTotal += int64(len(block.Data)) 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 { 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 blocks: %d\n", numBlocks)
fmt.Printf("num of bytes: %d\n", bytesTotal) fmt.Printf("num of bytes: %d\n", bytesTotal)
for version, count := range versions {
fmt.Printf("%d: %d\n", version, count)
}
} }