mirror of
https://github.com/derrod/legendary.git
synced 2024-09-30 17:27:36 +13:00
96 lines
2.5 KiB
Python
96 lines
2.5 KiB
Python
|
#!/usr/bin/env python
|
||
|
# coding: utf-8
|
||
|
|
||
|
import struct
|
||
|
import zlib
|
||
|
|
||
|
from io import BytesIO
|
||
|
|
||
|
|
||
|
# ToDo do some reworking to make this more memory efficient
|
||
|
|
||
|
class Chunk:
|
||
|
header_magic = 0xB1FE3AA2
|
||
|
|
||
|
def __init__(self):
|
||
|
self.header_version = 0
|
||
|
self.header_size = 0
|
||
|
self.compressed_size = 0
|
||
|
self.hash = 0
|
||
|
self.stored_as = 0
|
||
|
self.guid = []
|
||
|
|
||
|
self.hash_type = 0
|
||
|
self.sha_hash = None
|
||
|
self.uncompressed_size = 1024 * 1024
|
||
|
|
||
|
self._guid_str = ''
|
||
|
self._guid_num = 0
|
||
|
self._bio = None
|
||
|
self._data = None
|
||
|
|
||
|
@property
|
||
|
def data(self):
|
||
|
if self._data:
|
||
|
return self._data
|
||
|
|
||
|
if self.compressed:
|
||
|
self._data = zlib.decompress(self._bio.read())
|
||
|
else:
|
||
|
self._data = self._bio.read()
|
||
|
|
||
|
# close BytesIO with raw data since we no longer need it
|
||
|
self._bio.close()
|
||
|
self._bio = None
|
||
|
|
||
|
return self._data
|
||
|
|
||
|
@property
|
||
|
def guid_str(self):
|
||
|
if not self._guid_str:
|
||
|
self._guid_str = '-'.join('{:08x}'.format(g) for g in self.guid)
|
||
|
return self._guid_str
|
||
|
|
||
|
@property
|
||
|
def guid_num(self):
|
||
|
if not self._guid_num:
|
||
|
self._guid_num = self.guid[3] + (self.guid[2] << 32) + (self.guid[1] << 64) + (self.guid[0] << 96)
|
||
|
return self._guid_num
|
||
|
|
||
|
@property
|
||
|
def compressed(self):
|
||
|
return self.stored_as & 0x1
|
||
|
|
||
|
@classmethod
|
||
|
def read_buffer(cls, data):
|
||
|
_sio = BytesIO(data)
|
||
|
return cls.read(_sio)
|
||
|
|
||
|
@classmethod
|
||
|
def read(cls, bio):
|
||
|
head_start = bio.tell()
|
||
|
|
||
|
if struct.unpack('<I', bio.read(4))[0] != cls.header_magic:
|
||
|
raise ValueError('Chunk magic doesn\'t match!')
|
||
|
|
||
|
_chunk = cls()
|
||
|
_chunk._bio = bio
|
||
|
_chunk.header_version = struct.unpack('<I', bio.read(4))[0]
|
||
|
_chunk.header_size = struct.unpack('<I', bio.read(4))[0]
|
||
|
_chunk.compressed_size = struct.unpack('<I', bio.read(4))[0]
|
||
|
_chunk.guid = struct.unpack('<IIII', bio.read(16))
|
||
|
_chunk.hash = struct.unpack('<Q', bio.read(8))[0]
|
||
|
_chunk.stored_as = struct.unpack('B', bio.read(1))[0]
|
||
|
|
||
|
if _chunk.header_version >= 2:
|
||
|
_chunk.sha_hash = bio.read(20)
|
||
|
_chunk.hash_type = struct.unpack('B', bio.read(1))[0]
|
||
|
|
||
|
if _chunk.header_version >= 3:
|
||
|
_chunk.uncompressed_size = struct.unpack('<I', bio.read(4))[0]
|
||
|
|
||
|
if bio.tell() - head_start != _chunk.header_size:
|
||
|
raise ValueError('Did not read entire chunk header!')
|
||
|
|
||
|
return _chunk
|