legendary/legendary/models/chunk.py
derrod e00e534f2c First public test version of legendary
Unfortunately the history from before this commit is lost to time. And
that's probably for the best.
2020-04-14 15:40:41 +02:00

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