Last active
September 25, 2019 22:27
-
-
Save pyalot/5171175 to your computer and use it in GitHub Desktop.
Minecraft mca/nbt parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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