Example #1
0
    def __init__(self, stream):
        if stream.read_string() != b'UnityFS':
            raise ValueError('Not a UnityFS file!')
        version = stream.read_int()
        if version != 6:
            raise ValueError('Unsupported version: ' + str(version))
        mpv = stream.read_string()
        fev = stream.read_string()
        total_file_size = stream.read_long()
        compressed_size = stream.read_int()
        decompressed_size = stream.read_int()
        flags = stream.read_int()

        if flags & 0x80:
            if total_file_size == 0:
                bundle_info_offset = -1
            else:
                bundle_info_offset = total_file_size - compressed_size
        else:
            if flags & 0x100:
                bundle_info_offset = len(mpv) + len(fev) + 2 + 0x1A + 0x0A
            else:
                bundle_info_offset = len(mpv) + len(fev) + 0x1A + len(
                    'UnityFS') + 3

        compression_mode = flags & 0x3F
        dir_info = flags & 0x40
        bad_list = flags & 0x80
        blocks = []
        files = []

        block = stream.read_bytes(compressed_size)
        if compression_mode in (self.COMP_LZ4, self.COMP_LZ4HC):
            data = lz4.block.decompress(block,
                                        uncompressed_size=decompressed_size)
            s2 = Stream(data)
            s2.skip(16)
            block_count = s2.read_int()
            for _ in range(block_count):
                decomp_size = s2.read_int()
                comp_size = s2.read_int()
                flag = s2.read_short()
                blocks.append((comp_size, decomp_size))
            file_count = s2.read_int()
            for _ in range(file_count):
                f_offset = s2.read_long()
                decomp_size = s2.read_long()
                flag = s2.read_int()
                name = s2.read_string()
                files.append((f_offset, decomp_size, name))
        else:
            raise ValueError('Unsupported compression mode')

        block_data = io.BytesIO()
        for i in blocks:
            bd = stream.read_bytes(i[0])
            dc = lz4.block.decompress(bd, uncompressed_size=i[1])
            block_data.write(dc)

        self.files = {}
        for i in files:
            block_data.seek(i[0])
            self.files[i[2].decode()] = block_data.read(i[1])
Example #2
0
    def decode(self, obj, debug=False):
        object_id, data, type_tree = obj
        queue = list(type_tree[3])

        if debug:
            print('-' * 50)

        stream = Stream(data)
        stream.enidan = self.stream.enidan

        structure = {}
        current, align = [structure], [False]
        prev = None
        while queue:
            i = queue.pop(0)
            while i[0] != 0 and i[0] < len(current):
                current.pop(-1)
                if align.pop(-1):
                    t = (4 - (stream.tell() % 4)) % 4
                    assert stream.read_bytes(t) == b'\x00' * t

            multi = False
            if i[2] == b'int':
                d = stream.read_sint()
            elif i[2] == b'unsigned int':
                d = stream.read_int()
            elif i[2] == b'float':
                d = stream.read_float()
            elif i[2] == b'bool':
                d = stream.read_bool()
            elif i[2] == b'char':
                if isinstance(current[-1], list) and len(current[-1]) == 1:
                    multi = True
                    d = stream.read_bytes(current[-1][0])
                else:
                    d = stream.read_bytes(1)
            elif i[2] == b'UInt8':
                if isinstance(current[-1], list) and len(current[-1]) == 1:
                    multi = True
                    d = stream.read_bytes(current[-1][0])
                else:
                    d = stream.read_byte()
            elif i[1]:
                d = []
            else:
                d = None

            if debug:
                print(len(current),
                      str(stream.tell()).rjust(3),
                      ('  ' * i[0] + i[2].decode() + ': ').ljust(23) +
                      i[3].decode().ljust(25), hex(i[-1]),
                      str(d)[:30] + ('' if len(str(d)) <= 30 else '...'))

            if i[0] > len(current) and isinstance(current[-1], dict):
                current.append({})
                current[-2][prev[3].decode()] = current[-1]
                align.append(i[-1] & 0x4000)
            elif i[-1] & 0x4000 and not i[1]:
                t = (4 - (stream.tell() % 4)) % 4
                assert stream.read_bytes(t) == b'\x00' * t

            if isinstance(current[-1], list):
                if multi:
                    current[-1].extend(d)
                else:
                    current[-1].append(d)

                if current[-1] and len(current[-1]) == current[-1][0] + 1:
                    if not current[-1].pop(0):
                        queue.pop(0)
                    current.pop(-1)
                    if align.pop(-1):
                        t = (4 - (stream.tell() % 4)) % 4
                        assert stream.read_bytes(t) == b'\x00' * t
                elif len(current[-1]) > 1:
                    queue.insert(0, i)

            elif d is not None:
                current[-1][i[3].decode()] = d

            if isinstance(d, list):
                current.append(d)
                align.append(i[-1] & 0x4000)

            prev = i
        return structure