Beispiel #1
0
    def __encode(self, tree: Node, text_encoding: str, packet_encoding: int) -> bytes:
        """
        Given a Node tree, encode the data into the given packet encoding.

        Parameters:
            tree - A Node object representing the root of the tree to encode.
            text_encoding - The text encoding for any strings that will be encoded.
                            Should be EAmuseProtocol.SHIFT_JIS, EAmuseProtocol.EUC_JP or
                            EAmuseProtocol.UTF8.
            packet_encoding - The encoding used for the packet. Should be EAmuseProtocol.XML
                              or EAmuseProtocol.BINARY.

        Returns:
            A string blob representing the encoded packet.
        """
        if packet_encoding == EAmuseProtocol.BINARY:
            # It's binary, encode it
            binary = BinaryEncoding()
            return binary.encode(tree, encoding=text_encoding)
        elif packet_encoding == EAmuseProtocol.XML:
            # It's XML, encode it
            xml = XmlEncoding()
            return xml.encode(tree, encoding=text_encoding)
        else:
            raise EAmuseException(f"Invalid packet encoding {packet_encoding}")
Beispiel #2
0
    def __decode(self, data: bytes) -> Node:
        """
        Given data, decode the data into a Node tree.

        Parameters:
            data - Binary string representing data to decode.

        Returns:
            Node tree on success or None on failure.
        """
        # Assume it's a binary page
        binary = BinaryEncoding()
        ret = binary.decode(data, skip_on_exceptions=True)

        if ret is not None:
            # We got a result, it was binary
            self.last_text_encoding = binary.encoding
            self.last_packet_encoding = EAmuseProtocol.BINARY

            return ret

        # Assume its XML
        xml = XmlEncoding()
        ret = xml.decode(data, skip_on_exceptions=True)

        if ret is not None:
            # We got a result, it was XML
            self.last_text_encoding = xml.encoding
            self.last_packet_encoding = EAmuseProtocol.XML

            return ret

        # Couldn't decode
        raise EAmuseException('Unknown packet encoding')
Beispiel #3
0
    def __parse_file(self, data: bytes) -> None:
        # Grab the magic values and make sure this is an IFS
        (signature, version, version_crc, pack_time, unpacked_header_size,
         data_index) = struct.unpack(
             '>IHHIII',
             data[0:20],
         )
        if signature != 0x6CAD8F89:
            raise Exception('Invalid IFS file!')
        if version ^ version_crc != 0xFFFF:
            raise Exception('Corrupt version in IFS file!')

        if version == 1:
            # No header MD5
            header_offset = 20
        else:
            # Make room for header MD5, at byte offset 20-36
            header_offset = 36

        # First, try as binary
        benc = BinaryEncoding()
        header = benc.decode(data[header_offset:data_index])

        if header is None:
            # Now, try as XML
            xenc = XmlEncoding()
            header = xenc.decode(
                b'<?xml encoding="ascii"?>' +
                data[header_offset:data_index].split(b'\0')[0])

            if header is None:
                raise Exception('Invalid IFS file!')

        files: Dict[str, Tuple[int, int, int]] = {}

        if header.name != 'imgfs':
            raise Exception('Unknown IFS format!')

        def get_children(parent: str, node: Node) -> None:
            real_name = self.__fix_name(node.name)
            if node.data_type == '3s32':
                node_name = os.path.join(parent,
                                         real_name).replace('/imgfs/', '')
                files[node_name] = (node.value[0] + data_index, node.value[1],
                                    node.value[2])
            else:
                for subchild in node.children:
                    get_children(os.path.join(parent, "{}/".format(real_name)),
                                 subchild)

        get_children("/", header)

        for fn in files:
            (start, size, pack_time) = files[fn]
            filedata = data[start:(start + size)]
            self.__files[fn] = filedata

        if self.__decode_textures:
            # We must fix up the name of the textures since we're decoding them
            def fix_name(hashname: str) -> str:
                path = os.path.dirname(hashname)
                filename = os.path.basename(hashname)

                texlist = self.__get_texlist_for_file(hashname)

                if texlist is not None and texlist.name == 'texturelist':
                    for child in texlist.children:
                        if child.name != 'texture':
                            continue

                        textfmt = child.attribute('format')

                        for subchild in child.children:
                            if subchild.name != 'image':
                                continue
                            md5sum = hashlib.md5(
                                subchild.attribute('name').encode(
                                    benc.encoding)).hexdigest()

                            if md5sum == filename:
                                if textfmt == "argb8888rev":
                                    name = '{}.png'.format(
                                        subchild.attribute('name'))
                                else:
                                    name = subchild.attribute('name')
                                newpath = os.path.join(path, name)

                                rect = subchild.child_value('imgrect')
                                if rect is not None:
                                    self.__mappings[newpath] = textfmt
                                    self.__sizes[newpath] = (
                                        (rect[1] - rect[0]) // 2,
                                        (rect[3] - rect[2]) // 2,
                                    )

                                return newpath

                return hashname

            self.__files = {
                fix_name(fn): self.__files[fn]
                for fn in self.__files
            }