Skip to content

Instantly share code, notes, and snippets.

@pyalot
Last active September 25, 2019 22:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pyalot/5171175 to your computer and use it in GitHub Desktop.
Save pyalot/5171175 to your computer and use it in GitHub Desktop.
Minecraft mca/nbt parser
from struct import unpack
from zlib import decompress
class BufferStream:
def __init__(self, data):
self.pos = 0
self.data = data
def get(self, amount):
result = self.data[self.pos:self.pos+amount]
self.pos += amount
return result
@property
def byte(self):
return unpack('b', self.get(1))[0]
def bytes(self, size):
return unpack('%ib' % size, self.get(size))
@property
def short(self):
return unpack('>h', self.get(2))[0]
def shorts(self, size):
return unpack('>%ih' % size, self.get(size*2))
@property
def int(self):
return unpack('>i', self.get(4))[0]
def ints(self, size):
return unpack('>%ii' % size, self.get(size*4))
@property
def float(self):
return unpack('>f', self.get(4))[0]
def floats(self, size):
return unpack('>%if' % size, self.get(size*4))
@property
def long(self):
return unpack('>q', self.get(8))[0]
def longs(self, size):
return unpack('>%iq' % size, self.get(size*8))
@property
def double(self):
return unpack('>d', self.get(8))[0]
def doubles(self, size):
return unpack('>%id' % size, self.get(size*8))
def string(self, length):
return self.get(length).decode('utf-8')
def readType(type, stream):
if type == 1:
return stream.byte
elif type == 2:
return stream.short
elif type == 3:
return stream.int
elif type == 4:
return stream.long
elif type == 5:
return stream.float
elif type == 6:
return stream.double
elif type == 7:
size = stream.int
return stream.bytes(size)
elif type == 8:
size = stream.short
return stream.string(size)
elif type == 9:
return readList(stream)
elif type == 10:
return readDict(stream)
elif type == 11:
size = stream.int
return stream.ints(size)
def readList(stream):
type = stream.byte
size = stream.int
result = []
while len(result) < size:
result.append(readType(type, stream))
return result
def readDict(stream):
result = {}
while 1:
type = stream.byte
if type == 0: return result
name = stream.string(stream.short)
result[name] = readType(type, stream)
return result
def readNbt(nbt):
stream = BufferStream(nbt)
assert stream.byte == 10
assert stream.short == 0
return readDict(stream)
if __name__ == '__main__':
data = open('r.0.0.mca', 'rb').read()
locations = data[:1024*4]
for i in range(1024):
location = locations[i*4:i*4+4]
offset, count = unpack('>IB', '\0' + location)
offset *= 4096
if offset > 0:
bytecount, compressionType = unpack('>IB', data[offset:offset+5])
assert compressionType == 2
nbt = data[offset+5:offset+5+bytecount]
nbt = decompress(nbt)
nbt = readNbt(nbt)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment