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}")
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')
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 }