Blackthorne/Формат сжатия

Материал из Old-Games.RU Wiki
Перейти к навигации Перейти к поиску

Часть ресурсов в файле DATA.DAT упаковано модифицированным алгоритмом LZSS. Основных отличий два — биты в управляющем байте развернуты задом наперед (то есть 8-й бит — это первый байт в потоке) и к размеру копируемых данных из буфера добавляется 3.

Пример на Python:

def bits(byte):
    # Backward access
    return (
        byte & 1,
        (byte >> 1) & 1,
        (byte >> 2) & 1,
        (byte >> 3) & 1,
        (byte >> 4) & 1,
        (byte >> 5) & 1,
        (byte >> 6) & 1,
        (byte >> 7) & 1,
    )


def control(word):
    return (
        (word >> 12) + 3,       # Blocksize (3 is custom from Blizzard)
        word & 0x0fff           # Position on buffer
    )


def LZSS_BT_Unpack(stream):
    unpacked_size = Int32ul.parse(stream[0:4])

    buffer = bytearray(4096)
    buffer_pos = 0
    unpacked_pos = 0
    # first 4 bytes - size of unpacked data
    pos = 4

    unpacked = bytearray()
    while pos < len(stream):
        # First byte is control byte. Each bit controls what is next byte would be:
        # 1 - non-compressed, 0 - compressed 2-byte
        control_byte = bits(stream[pos])
        pos += 1
        for bit in control_byte:
            # Non-compressed data
            if bit == 1:
                buffer[buffer_pos] = stream[pos]
                buffer_pos += 1
                if buffer_pos >= len(buffer):
                    buffer_pos -= len(buffer)
                unpacked.append(stream[pos])
                pos += 1
                unpacked_pos += 1
                if unpacked_pos >= unpacked_size:
                    break
            # Compressed data
            else:
                block_size, buffer_offset = control(Int16ul.parse(stream[pos:pos + 2]))
                pos += 2
                i = 0
                while i < block_size:
                    buffer[buffer_pos] = buffer[buffer_offset]
                    buffer_pos += 1
                    if buffer_pos >= len(buffer):
                        buffer_pos -= len(buffer)
                    unpacked.append(buffer[buffer_offset])
                    unpacked_pos += 1
                    buffer_offset += 1
                    if buffer_offset >= len(buffer):
                        buffer_offset -= len(buffer)
                    i += 1
                if unpacked_pos >= unpacked_size:
                    break

    return unpacked