def _decompress_xpress(self, reader: StructReader, writer: MemoryFile, target: Optional[int] = None) -> bytearray: if target is not None: target += writer.tell() flags = BitBufferedReader(reader) nibble_cache = None while not reader.eof: if target is not None and writer.tell() >= target: return if not flags.next(): writer.write(reader.read(1)) continue offset, length = divmod(reader.u16(), 8) offset += 1 if length == 7: length = nibble_cache if length is None: length_pair = reader.u8() nibble_cache = length_pair >> 4 length = length_pair & 0xF else: nibble_cache = None if length == 15: length = reader.u8() if length == 0xFF: length = reader.u16() or reader.u32() length -= 22 if length < 0: raise RuntimeError( F'Invalid match length of {length} for long delta sequence' ) length += 15 length += 7 length += 3 writer.replay(offset, length)
def _decompress(self, writer: MemoryFile, reader_: StructReader[bytearray], size: Optional[int] = None): index = 1 base = 8 literal_bits = None literal_offset = None flags = BitBufferedReader(reader_, 32) while True: if size and len(writer) >= size: break if flags.next(): b = flags.read(literal_bits) + literal_offset b = b & 0xFF writer.write_byte(b) continue if flags.next(): high = flags.variable_length_integer() if (high == 2): match_length = flags.variable_length_integer() else: index = ((high - 3) << base) + flags.read(base) match_length = flags.variable_length_integer() if index >= 0x10000: match_length += 3 elif index >= 0x37FF: match_length += 2 elif index >= 0x27F: match_length += 1 elif index <= 127: match_length += 4 writer.replay(index, match_length) continue if not flags.next(): new_index = flags.read(7) match_length = 2 + flags.read(2) if new_index == 0: if match_length == 2: break base = flags.read(match_length + 1) else: index = new_index writer.replay(index, match_length) continue one_byte_phrase_value = flags.read(4) - 1 if one_byte_phrase_value == 0: writer.write_byte(0) elif one_byte_phrase_value > 0: b = writer.getbuffer()[-one_byte_phrase_value] writer.write_byte(b) else: if not flags.next(): literal_bits = 7 + flags.next() literal_offset = 0 if literal_bits != 8: literal_offset = flags.read(8) continue while True: for _ in range(0x100): b = flags.read(8) writer.write_byte(b) if not flags.next(): break
def _decompress_xpress_huffman(self, reader: StructReader, writer: MemoryFile, target: Optional[int] = None, max_chunk_size: int = 0x10000) -> None: limit = writer.tell() if target is not None: target += limit while not reader.eof: if reader.remaining_bytes < XPRESS_NUM_SYMBOLS // 2: raise IndexError( F'There are only {reader.remaining_bytes} bytes reamining in the input buffer,' F' but at least {XPRESS_NUM_SYMBOLS//2} are required to read a Huffman table.' ) table = bytearray( reader.read_integer(4) for _ in range(XPRESS_NUM_SYMBOLS)) table = make_huffman_decode_table(table, XPRESS_TABLEBITS, XPRESS_MAX_CODEWORD_LEN) limit = limit + max_chunk_size flags = BitBufferedReader(reader, 16) while True: position = writer.tell() if position == target: if reader.remaining_bytes: self.log_info( F'chunk decompressed with {reader.remaining_bytes} bytes remaining in input buffer' ) return if position >= limit: if position > limit: limit = position self.log_info( F'decompression of one chunk generated more than the limit of {max_chunk_size} bytes' ) flags.collect() break try: sym = flags.huffman_symbol(table, XPRESS_TABLEBITS, XPRESS_MAX_CODEWORD_LEN) except EOFError: self.log_debug('end of file while reading huffman symbol') break if sym < XPRESS_NUM_CHARS: writer.write_byte(sym) continue length = sym & 0xF offsetlog = (sym >> 4) & 0xF flags.collect() if reader.eof: break offset = (1 << offsetlog) | flags.read(offsetlog) if length == 0xF: nudge = reader.read_byte() if nudge < 0xFF: length += nudge else: length = reader.u16() or reader.u32() length += XPRESS_MIN_MATCH_LEN writer.replay(offset, length)