Module refinery.lib.fast.a3x

Expand source code Browse git
from __future__ import annotations


def a3x_decompress(data: bytearray | memoryview, is_current: bool) -> bytearray:
    view = memoryview(data)
    size = int.from_bytes(view[4:8], 'big')
    src = bytes(view[8:])
    src_len = len(src)
    src_pos = 0
    bit_buffer = 0
    bit_count = 0
    output = bytearray()

    def _bits(n: int) -> int:
        nonlocal src_pos, bit_buffer, bit_count
        while bit_count < n:
            if src_pos >= src_len:
                raise EOFError
            bit_buffer = (bit_buffer << 8) | src[src_pos]
            src_pos += 1
            bit_count += 8
        bit_count -= n
        result = (bit_buffer >> bit_count) & ((1 << n) - 1)
        bit_buffer &= (1 << bit_count) - 1
        return result

    cursor = 0
    while cursor < size:
        check = _bits(1)
        if check == is_current:
            output.append(_bits(8))
            cursor += 1
            continue
        offset = _bits(15)
        length = _bits(2)
        delta = 0
        if length == 0b11:
            delta = 0x003
            length = _bits(3)
            if length == 0b111:
                delta = 0x00A
                length = _bits(5)
                if length == 0b11111:
                    delta = 0x029
                    length = _bits(8)
                    if length == 0b11111111:
                        delta = 0x128
                        length = _bits(8)
        while length == 0b11111111:
            delta += 0xFF
            length = _bits(8)
        length += delta + 3
        length &= 0xFFFFFFFF
        # replay: copy `length` bytes from `offset` bytes back in the output
        out_len = len(output)
        start = out_len - offset
        if offset <= 0 or start < 0:
            raise ValueError(
                F'Invalid back-reference: offset={offset}, output_size={out_len}')
        rep, r = divmod(length, offset)
        chunk = bytes(output[start:out_len])
        if rep > 0:
            output.extend(chunk * rep)
        if r > 0:
            output.extend(chunk[:r])
        cursor += length

    return output


def a3x_decrypt_current(data: memoryview | bytearray, key: int) -> bytearray:
    a, b, t = 16, 6, []

    for _ in range(17):
        key = 1 - key * 0x53A9B4FB & 0xFFFFFFFF
        t.append(key)

    t.reverse()

    for _ in range(9):
        r = (t[a] << 9 | t[a] >> 23) + (t[b] << 13 | t[b] >> 19) & 0xFFFFFFFF
        t[a] = r
        a = (a + 1) % 17
        b = (b + 1) % 17

    def _decrypted():
        nonlocal a, b
        for v in data:
            x = t[a]
            y = t[b]
            t[a] = (x << 9 | x >> 23) + (y << 13 | y >> 19) & 0xFFFFFFFF
            a = (a + 1) % 17
            b = (b + 1) % 17
            x = t[a]
            y = t[b]
            r = (x << 9 | x >> 23) + (y << 13 | y >> 19) & 0xFFFFFFFF
            t[a] = r
            a = (a + 1) % 17
            b = (b + 1) % 17
            yield (r >> 24) ^ v

    return bytearray(_decrypted())


def a3x_decrypt_legacy(data: bytearray | memoryview, key: int) -> bytearray:
    a, b, t = 1, 0, []

    t.append(key)
    for i in range(1, 624):
        key = ((((key ^ key >> 30) * 0x6C078965) & 0xFFFFFFFF) + i) & 0xFFFFFFFF
        t.append(key)

    def _refactor_state():
        for i in range(0, 0xe3):
            x = t[i] ^ t[i + 1]
            x &= 0x7FFFFFFE
            x ^= t[i]
            x >>= 1
            y = 0x9908B0DF
            if (t[i + 1] % 2 == 0):
                y = 0
            x ^= y
            x ^= t[i + 397]
            t[i] = x

        for i in range(0xe3, 0x18c + 0xe3):
            x = t[i] ^ t[i + 1]
            x &= 0x7FFFFFFE
            x ^= t[i]
            x >>= 1
            y = 0x9908B0DF
            if (t[i + 1] % 2 == 0):
                y = 0
            x ^= y
            x ^= t[i - 227]
            t[i] = x

        x = t[0]
        y = t[0x18c + 0xe3] ^ x
        y &= 0x7FFFFFFE
        y ^= t[0x18c + 0xe3]
        y >>= 1
        if (x % 2 == 1):
            x = 0x9908B0DF
        else:
            x = 0
        y ^= x
        y ^= t[0x18c + 0xe3 - 227]
        t[0x18c + 0xe3] = y

    def _decrypted():
        nonlocal a, b
        for v in data:
            a -= 1
            b += 1
            if a == 0:
                a = 0x270
                b = 0
                _refactor_state()
            x = t[b]
            x = x ^ x >> 11
            y = ((x & 0xFF3A58AD) << 7) & 0xFFFFFFFF
            x ^= y
            y = ((x & 0xFFFFDF8C) << 15) & 0xFFFFFFFF
            x ^= y
            y = x ^ x >> 0x12
            yield ((y >> 1) ^ v) & 0xFF

    return bytearray(_decrypted())


def a3x_decrypt(data: memoryview | bytearray, key: int, is_current: bool = True) -> bytearray:
    if is_current:
        return a3x_decrypt_current(data, key)
    return a3x_decrypt_legacy(data, key)

Functions

def a3x_decompress(data, is_current)
Expand source code Browse git
def a3x_decompress(data: bytearray | memoryview, is_current: bool) -> bytearray:
    view = memoryview(data)
    size = int.from_bytes(view[4:8], 'big')
    src = bytes(view[8:])
    src_len = len(src)
    src_pos = 0
    bit_buffer = 0
    bit_count = 0
    output = bytearray()

    def _bits(n: int) -> int:
        nonlocal src_pos, bit_buffer, bit_count
        while bit_count < n:
            if src_pos >= src_len:
                raise EOFError
            bit_buffer = (bit_buffer << 8) | src[src_pos]
            src_pos += 1
            bit_count += 8
        bit_count -= n
        result = (bit_buffer >> bit_count) & ((1 << n) - 1)
        bit_buffer &= (1 << bit_count) - 1
        return result

    cursor = 0
    while cursor < size:
        check = _bits(1)
        if check == is_current:
            output.append(_bits(8))
            cursor += 1
            continue
        offset = _bits(15)
        length = _bits(2)
        delta = 0
        if length == 0b11:
            delta = 0x003
            length = _bits(3)
            if length == 0b111:
                delta = 0x00A
                length = _bits(5)
                if length == 0b11111:
                    delta = 0x029
                    length = _bits(8)
                    if length == 0b11111111:
                        delta = 0x128
                        length = _bits(8)
        while length == 0b11111111:
            delta += 0xFF
            length = _bits(8)
        length += delta + 3
        length &= 0xFFFFFFFF
        # replay: copy `length` bytes from `offset` bytes back in the output
        out_len = len(output)
        start = out_len - offset
        if offset <= 0 or start < 0:
            raise ValueError(
                F'Invalid back-reference: offset={offset}, output_size={out_len}')
        rep, r = divmod(length, offset)
        chunk = bytes(output[start:out_len])
        if rep > 0:
            output.extend(chunk * rep)
        if r > 0:
            output.extend(chunk[:r])
        cursor += length

    return output
def a3x_decrypt_current(data, key)
Expand source code Browse git
def a3x_decrypt_current(data: memoryview | bytearray, key: int) -> bytearray:
    a, b, t = 16, 6, []

    for _ in range(17):
        key = 1 - key * 0x53A9B4FB & 0xFFFFFFFF
        t.append(key)

    t.reverse()

    for _ in range(9):
        r = (t[a] << 9 | t[a] >> 23) + (t[b] << 13 | t[b] >> 19) & 0xFFFFFFFF
        t[a] = r
        a = (a + 1) % 17
        b = (b + 1) % 17

    def _decrypted():
        nonlocal a, b
        for v in data:
            x = t[a]
            y = t[b]
            t[a] = (x << 9 | x >> 23) + (y << 13 | y >> 19) & 0xFFFFFFFF
            a = (a + 1) % 17
            b = (b + 1) % 17
            x = t[a]
            y = t[b]
            r = (x << 9 | x >> 23) + (y << 13 | y >> 19) & 0xFFFFFFFF
            t[a] = r
            a = (a + 1) % 17
            b = (b + 1) % 17
            yield (r >> 24) ^ v

    return bytearray(_decrypted())
def a3x_decrypt_legacy(data, key)
Expand source code Browse git
def a3x_decrypt_legacy(data: bytearray | memoryview, key: int) -> bytearray:
    a, b, t = 1, 0, []

    t.append(key)
    for i in range(1, 624):
        key = ((((key ^ key >> 30) * 0x6C078965) & 0xFFFFFFFF) + i) & 0xFFFFFFFF
        t.append(key)

    def _refactor_state():
        for i in range(0, 0xe3):
            x = t[i] ^ t[i + 1]
            x &= 0x7FFFFFFE
            x ^= t[i]
            x >>= 1
            y = 0x9908B0DF
            if (t[i + 1] % 2 == 0):
                y = 0
            x ^= y
            x ^= t[i + 397]
            t[i] = x

        for i in range(0xe3, 0x18c + 0xe3):
            x = t[i] ^ t[i + 1]
            x &= 0x7FFFFFFE
            x ^= t[i]
            x >>= 1
            y = 0x9908B0DF
            if (t[i + 1] % 2 == 0):
                y = 0
            x ^= y
            x ^= t[i - 227]
            t[i] = x

        x = t[0]
        y = t[0x18c + 0xe3] ^ x
        y &= 0x7FFFFFFE
        y ^= t[0x18c + 0xe3]
        y >>= 1
        if (x % 2 == 1):
            x = 0x9908B0DF
        else:
            x = 0
        y ^= x
        y ^= t[0x18c + 0xe3 - 227]
        t[0x18c + 0xe3] = y

    def _decrypted():
        nonlocal a, b
        for v in data:
            a -= 1
            b += 1
            if a == 0:
                a = 0x270
                b = 0
                _refactor_state()
            x = t[b]
            x = x ^ x >> 11
            y = ((x & 0xFF3A58AD) << 7) & 0xFFFFFFFF
            x ^= y
            y = ((x & 0xFFFFDF8C) << 15) & 0xFFFFFFFF
            x ^= y
            y = x ^ x >> 0x12
            yield ((y >> 1) ^ v) & 0xFF

    return bytearray(_decrypted())
def a3x_decrypt(data, key, is_current=True)
Expand source code Browse git
def a3x_decrypt(data: memoryview | bytearray, key: int, is_current: bool = True) -> bytearray:
    if is_current:
        return a3x_decrypt_current(data, key)
    return a3x_decrypt_legacy(data, key)