104 lines
2.1 KiB
Go
104 lines
2.1 KiB
Go
package anvil
|
|
|
|
import (
|
|
"compress/gzip"
|
|
"compress/zlib"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/admin-else/strom/nbt"
|
|
)
|
|
|
|
type CompressionStrategy uint8
|
|
|
|
const (
|
|
ChunksWidthRegionFile = 32
|
|
ChunksRegionFile = ChunksWidthRegionFile * ChunksWidthRegionFile
|
|
)
|
|
|
|
const (
|
|
_ CompressionStrategy = iota
|
|
CompressionStrategyGZIP
|
|
CompressionStrategyZLIB
|
|
CompressionStrategyNONE
|
|
CompressionStrategyLZ4
|
|
)
|
|
|
|
func (c CompressionStrategy) Wrap(r io.ReadCloser) (rNew io.ReadCloser, shouldClose bool, err error) {
|
|
switch c {
|
|
case CompressionStrategyGZIP:
|
|
shouldClose = true
|
|
rNew, err = gzip.NewReader(r)
|
|
return
|
|
case CompressionStrategyZLIB:
|
|
shouldClose = true
|
|
rNew, err = zlib.NewReader(r)
|
|
return
|
|
case CompressionStrategyNONE:
|
|
rNew = r
|
|
return
|
|
case CompressionStrategyLZ4:
|
|
err = fmt.Errorf("lz4 compression not supported")
|
|
return
|
|
default:
|
|
err = fmt.Errorf("unknown compression strategy %d", c)
|
|
return
|
|
}
|
|
}
|
|
|
|
type ChunkHeader struct {
|
|
Chunk, Time [ChunksRegionFile]int32
|
|
}
|
|
|
|
func ReadChunkHeader(r *os.File) (ch *ChunkHeader, err error) {
|
|
var h ChunkHeader
|
|
err = binary.Read(r, binary.BigEndian, &h)
|
|
if err != nil {
|
|
return
|
|
}
|
|
ch = &h
|
|
return
|
|
}
|
|
|
|
func (c *ChunkHeader) ChunkAt(f *os.File, x, z int32) (n *nbt.Tag, err error) {
|
|
off, size := chunkLocation(c.Chunk[chunkXZtoIndex(x, z)])
|
|
_, err = f.Seek(int64(off), 0)
|
|
if err != nil {
|
|
return
|
|
}
|
|
_ = size // i dont really know why mc has this maybe to point out how much space is wasted?
|
|
var realSize int32
|
|
err = binary.Read(f, binary.BigEndian, &realSize)
|
|
if err != nil {
|
|
return
|
|
}
|
|
var compressionStrategy CompressionStrategy
|
|
err = binary.Read(f, binary.BigEndian, &compressionStrategy)
|
|
if err != nil {
|
|
return
|
|
}
|
|
r, shouldClose, err := compressionStrategy.Wrap(f)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if shouldClose {
|
|
defer r.Close()
|
|
}
|
|
n = &nbt.Tag{}
|
|
err = n.Decode(r)
|
|
if err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func chunkXZtoIndex(x, z int32) int32 {
|
|
return (x % ChunksWidthRegionFile) + (z%ChunksWidthRegionFile)*ChunksWidthRegionFile
|
|
}
|
|
|
|
func chunkLocation(v int32) (offset, l int32) {
|
|
return ((v >> 8) & 0xFFFFFF) * 4096, (v & 0xFF) * 4096
|
|
}
|