Mercurial > hg > icedrobot > daneel
changeset 5:bfd49213d13c
Prototype for a class transformer using the ASM library. Contributed by Michael Starzinger.
author | Mario Torre <neugens.limasoftware@gmail.com> |
---|---|
date | Mon, 07 Mar 2011 20:38:30 +0100 |
parents | 1308ee8666a2 |
children | 2672c39e014c |
files | pom.xml src/main/java/org/icedrobot/daneel/ClassTransformer.java src/main/java/org/icedrobot/daneel/dex/ClassData.java src/main/java/org/icedrobot/daneel/dex/ClassDef.java src/main/java/org/icedrobot/daneel/dex/DexFile.java src/main/java/org/icedrobot/daneel/dex/FieldId.java src/main/java/org/icedrobot/daneel/util/BufferUtil.java src/test/java/org/icedrobot/daneel/ClassTransformerTest.java |
diffstat | 8 files changed, 603 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/pom.xml Mon Mar 07 20:30:39 2011 +0100 +++ b/pom.xml Mon Mar 07 20:38:30 2011 +0100 @@ -46,6 +46,11 @@ </build> <dependencies> <dependency> + <groupId>asm</groupId> + <artifactId>asm</artifactId> + <version>3.3</version> + </dependency> + <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.2</version>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/org/icedrobot/daneel/ClassTransformer.java Mon Mar 07 20:38:30 2011 +0100 @@ -0,0 +1,85 @@ +/* + * Daneel - Dalvik to Java bytecode compiler + * Copyright (C) 2011 IcedRobot team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package org.icedrobot.daneel; + +import org.icedrobot.daneel.dex.ClassData; +import org.icedrobot.daneel.dex.ClassDef; +import org.icedrobot.daneel.dex.FieldId; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; + +public class ClassTransformer { + + /** + * Transforms a given class from Dalvik representation into its equivalent + * Java bytecode representation. + * @param classDef The given class definition as Dalvik representation. + * @return The generated Java bytecode representation. + */ + public static byte[] transformClass(ClassDef classDef) { + ClassTransformer transformer = new ClassTransformer(classDef); + transformer.visitClass(); + transformer.visitFields(); + return transformer.visitEnd(); + } + + private ClassWriter writer; + + private ClassDef classDef; + + private ClassTransformer(ClassDef classDef) { + super(); + this.writer = new ClassWriter(0); + this.classDef = classDef; + } + + private void visitClass() { + int access = classDef.getAccessFlags(); + String name = extractName(classDef.getClassName()); + String superName = extractName(classDef.getSuperclass()); + writer.visit(Opcodes.V1_6, access, name, null, superName, null); + } + + private void visitFields() { + ClassData classData = classDef.getClassData(); + for (int i = 0; i < classData.getStaticFieldsSize(); i++) { + int access = classData.getStaticFieldsFlag(i); + FieldId field = classData.getStaticFieldsId(i); + writer.visitField(access, field.getName(), field.getTypeDescriptor(), null, null); + } + } + + private byte[] visitEnd() { + writer.visitEnd(); + return writer.toByteArray(); + } + + /** + * Converts a type descriptor for a class type into an internal class name + * as the ASM library expects it. For details about those type names see + * the "ASM 3.0 User Guide, Section 2.1.2 and 2.1.3". + * @param desc The given type descriptor. + * @return The internal class name. + */ + private static String extractName(String desc) { + if (!desc.startsWith("L") || !desc.endsWith(";")) + throw new DaneelException("Descriptor is not a class type."); + return desc.substring(1, desc.length() - 1); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/org/icedrobot/daneel/dex/ClassData.java Mon Mar 07 20:38:30 2011 +0100 @@ -0,0 +1,148 @@ +/* + * Daneel - Dalvik to Java bytecode compiler + * Copyright (C) 2011 IcedRobot team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package org.icedrobot.daneel.dex; + +import java.nio.ByteBuffer; + +import org.icedrobot.daneel.util.BufferUtil; + +public class ClassData { + + /** + * Parses a {@code class_data_item} structure in a DEX file at the buffer's + * current position. + * @param buffer The byte buffer to read from. + * @param dex The DEX file currently being parsed. + * @return An object representing the parsed data. + */ + static ClassData parse(ByteBuffer buffer, DexFile dex) { + return new ClassData(buffer, dex); + } + + private int staticFieldsSize; + + private int instanceFieldsSize; + + private int directMethodsSize; + + private int virtualMethodsSize; + + private FieldId[] staticFieldsIds; + private int[] staticFieldsFlags; + + private FieldId[] instanceFieldsIds; + private int[] instanceFieldsFlags; + + private int[] directMethodsIdxDiff; + private int[] directMethodsFlags; + private int[] directMethodsCodeOff; + + private int[] virtualMethodsIdxDiff; + private int[] virtualMethodsFlags; + private int[] virtualMethodsCodeOff; + + private ClassData(ByteBuffer buffer, DexFile dex) { + staticFieldsSize = BufferUtil.getULEB128(buffer); + instanceFieldsSize = BufferUtil.getULEB128(buffer); + directMethodsSize = BufferUtil.getULEB128(buffer); + virtualMethodsSize = BufferUtil.getULEB128(buffer); + + // Parse encoded_field structures in static_fields array and resolve + // field id indices. + staticFieldsIds = new FieldId[staticFieldsSize]; + staticFieldsFlags = new int[staticFieldsSize]; + int staticFieldsIdx = 0; + for (int i = 0; i < staticFieldsSize; i++) { + staticFieldsIdx += BufferUtil.getULEB128(buffer); + staticFieldsIds[i] = dex.getFieldId(staticFieldsIdx); + staticFieldsFlags[i] = BufferUtil.getULEB128(buffer); + } + + // Parse encoded_field structures in instance_fields array and resolve + // field id indices. + instanceFieldsIds = new FieldId[instanceFieldsSize]; + instanceFieldsFlags = new int[instanceFieldsSize]; + int instanceFieldsIdx = 0; + for (int i = 0; i < instanceFieldsSize; i++) { + instanceFieldsIdx += BufferUtil.getULEB128(buffer); + instanceFieldsIds[i] = dex.getFieldId(instanceFieldsIdx); + instanceFieldsFlags[i] = BufferUtil.getULEB128(buffer); + } + + // Parse encoded_method structures in direct_methods array. + directMethodsIdxDiff = new int[directMethodsSize]; + directMethodsFlags = new int[directMethodsSize]; + directMethodsCodeOff = new int[directMethodsSize]; + for (int i = 0; i < directMethodsSize; i++) { + directMethodsIdxDiff[i] = BufferUtil.getULEB128(buffer); + directMethodsFlags[i] = BufferUtil.getULEB128(buffer); + directMethodsCodeOff[i] = BufferUtil.getULEB128(buffer); + } + + // Parse encoded_method structures in virtual_methods array. + virtualMethodsIdxDiff = new int[virtualMethodsSize]; + virtualMethodsFlags = new int[virtualMethodsSize]; + virtualMethodsCodeOff = new int[virtualMethodsSize]; + for (int i = 0; i < virtualMethodsSize; i++) { + virtualMethodsIdxDiff[i] = BufferUtil.getULEB128(buffer); + virtualMethodsFlags[i] = BufferUtil.getULEB128(buffer); + virtualMethodsCodeOff[i] = BufferUtil.getULEB128(buffer); + } + } + + public int getStaticFieldsSize() { + return staticFieldsSize; + } + + public int getInstanceFieldsSize() { + return instanceFieldsSize; + } + + public int getDirectMethodsSize() { + return directMethodsSize; + } + + public int getVirtualMethodsSize() { + return virtualMethodsSize; + } + + public FieldId getStaticFieldsId(int idx) { + return staticFieldsIds[idx]; + } + + public int getStaticFieldsFlag(int idx) { + return staticFieldsFlags[idx]; + } + + public FieldId getInstanceFieldsId(int idx) { + return instanceFieldsIds[idx]; + } + + public int getInstanceFieldsFlag(int idx) { + return instanceFieldsFlags[idx]; + } + + public int getDirectMethodsFlag(int idx) { + return directMethodsFlags[idx]; + } + + public int getVirtualMethodsFlag(int idx) { + return virtualMethodsFlags[idx]; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/org/icedrobot/daneel/dex/ClassDef.java Mon Mar 07 20:38:30 2011 +0100 @@ -0,0 +1,102 @@ +/* + * Daneel - Dalvik to Java bytecode compiler + * Copyright (C) 2011 IcedRobot team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package org.icedrobot.daneel.dex; + +import java.nio.ByteBuffer; + +public class ClassDef { + + /** + * Parses a {@code class_def_item} structure in a DEX file at the buffer's + * current position. + * @param buffer The byte buffer to read from. + * @param dex The DEX file currently being parsed. + * @return An object representing the parsed data. + */ + static ClassDef parse(ByteBuffer buffer, DexFile dex) { + return new ClassDef(buffer, dex); + } + + private String className; + + private int accessFlags; + + private String superclass; + + private String[] interfaces; + + private String sourceFile; + + private ClassData classData; + + private ClassDef(ByteBuffer buffer, DexFile dex) { + int classIdx = buffer.getInt(); + accessFlags = buffer.getInt(); + int superclassIdx = buffer.getInt(); + int interfacesOff = buffer.getInt(); + int sourceFileIdx = buffer.getInt(); + int annotationsOff = buffer.getInt(); + int classDataOff = buffer.getInt(); + int staticValuesOff = buffer.getInt(); + + // Resolve string and type indices. + className = dex.getTypeDescriptor(classIdx); + if (superclassIdx != DexFile.NO_INDEX) + superclass = dex.getTypeDescriptor(superclassIdx); + if (sourceFileIdx != DexFile.NO_INDEX) + sourceFile = dex.getString(sourceFileIdx); + + // Parse associated type_list structure and resolve type indices. + ByteBuffer buf = buffer.duplicate(); + if (interfacesOff != 0) { + buf.position(interfacesOff); + interfaces = new String[buf.getInt()]; + for (int i = 0; i < interfaces.length; i++) + interfaces[i] = dex.getTypeDescriptor(buf.getShort()); + } + + // Parse the associated class_data_item structure. + buf.position(classDataOff); + classData = ClassData.parse(buf, dex); + } + + public String getClassName() { + return className; + } + + public int getAccessFlags() { + return accessFlags; + } + + public String getSuperclass() { + return superclass; + } + + public String[] getInterfaces() { + return interfaces; + } + + public String getSourceFile() { + return sourceFile; + } + + public ClassData getClassData() { + return classData; + } +}
--- a/src/main/java/org/icedrobot/daneel/dex/DexFile.java Mon Mar 07 20:30:39 2011 +0100 +++ b/src/main/java/org/icedrobot/daneel/dex/DexFile.java Mon Mar 07 20:38:30 2011 +0100 @@ -2,23 +2,86 @@ import java.nio.ByteBuffer; -class DexFile { +import org.icedrobot.daneel.util.BufferUtil; + +public class DexFile { + + /** Constant used to indicate that an index value is absent. */ + public static final int NO_INDEX = 0xffffffff; - static DexFile parse(ByteBuffer buf) { + public static DexFile parse(ByteBuffer buf) { return new DexFile(buf); } + public static DexFile parse(byte[] bytes) { + return new DexFile(ByteBuffer.wrap(bytes)); + } + private Header header; - private DexFile(ByteBuffer buf) { - ByteBuffer headerBuffer = buf.duplicate(); - header = Header.parse(headerBuffer); + private String[] strings; + + private String[] typeDescriptors; + + private FieldId[] fieldIds; + + private ClassDef[] classDefs; + + private DexFile(ByteBuffer buffer) { + ByteBuffer buf = buffer.duplicate(); + header = Header.parse(buf); + + // Parse string_id_item and string_data_item structures. + strings = new String[header.getStringIdsSize()]; + buf.position(header.getStringIdsOff()); + int[] stringsDataOff = BufferUtil.getInts(buf, strings.length); + for (int i = 0; i < strings.length; i++) { + buf.position(stringsDataOff[i]); + int utf16Size = BufferUtil.getULEB128(buf); + strings[i] = BufferUtil.getMUTF8(buf); + if (strings[i].length() != utf16Size) + throw new DexParseException("Mismatch in string lengths."); + } + + // Parse type_id_item structures. + typeDescriptors = new String[header.getTypeIdsSize()]; + buf.position(header.getTypeIdsOff()); + for (int i = 0; i < typeDescriptors.length; i++) + typeDescriptors[i] = getString(buf.getInt()); + + // Parse the field_id_item structures. + fieldIds = new FieldId[header.getFieldIdsSize()]; + buf.position(header.getFieldIdsOff()); + for (int i = 0; i < fieldIds.length; i++) + fieldIds[i] = FieldId.parse(buf, this); + + // Parse class_def_item structures. + classDefs = new ClassDef[header.getClassDefsSize()]; + buf.position(header.getClassDefsOff()); + for (int i = 0; i < classDefs.length; i++) + classDefs[i] = ClassDef.parse(buf, this); } Header getHeader() { return header; } + public String getString(int idx) { + return strings[idx]; + } + + public String getTypeDescriptor(int idx) { + return typeDescriptors[idx]; + } + + public FieldId getFieldId(int idx) { + return fieldIds[idx]; + } + + public ClassDef getClassDef(int idx) { + return classDefs[idx]; + } + public String toString() { return header.toString(); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/org/icedrobot/daneel/dex/FieldId.java Mon Mar 07 20:38:30 2011 +0100 @@ -0,0 +1,59 @@ +/* + * Daneel - Dalvik to Java bytecode compiler + * Copyright (C) 2011 IcedRobot team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package org.icedrobot.daneel.dex; + +import java.nio.ByteBuffer; + +public class FieldId { + + /** + * Parses a {@code field_id_item} structure in a DEX file at the buffer's + * current position. + * @param buffer The byte buffer to read from. + * @param dex The DEX file currently being parsed. + * @return An object representing the parsed data. + */ + static FieldId parse(ByteBuffer buffer, DexFile dex) { + return new FieldId(buffer, dex); + } + + private final String className; + + private final String typeDescriptor; + + private final String name; + + private FieldId(ByteBuffer buffer, DexFile dex) { + className = dex.getTypeDescriptor(buffer.getShort()); + typeDescriptor = dex.getTypeDescriptor(buffer.getShort()); + name = dex.getString(buffer.getInt()); + } + + public String getClassName() { + return className; + } + + public String getTypeDescriptor() { + return typeDescriptor; + } + + public String getName() { + return name; + } +}
--- a/src/main/java/org/icedrobot/daneel/util/BufferUtil.java Mon Mar 07 20:30:39 2011 +0100 +++ b/src/main/java/org/icedrobot/daneel/util/BufferUtil.java Mon Mar 07 20:38:30 2011 +0100 @@ -18,7 +18,9 @@ package org.icedrobot.daneel.util; +import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; +import java.nio.charset.Charset; public class BufferUtil { @@ -53,4 +55,21 @@ } return result; } + + /** + * Reads string encoded using the "Modified UTF-8" encoding at the buffer's + * current position. The number of bytes read from the buffer is implied by + * the position of the terminating 0 byte. + * @param buffer The byte buffer to read from. + * @return The decoded string value. + */ + public static String getMUTF8(ByteBuffer buffer) { + byte b; + ByteArrayOutputStream tmp = new ByteArrayOutputStream(); + while ((b = buffer.get()) != 0) + tmp.write(b); + return new String(tmp.toByteArray(), UTF8); + } + + private static final Charset UTF8 = Charset.forName("UTF-8"); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/test/java/org/icedrobot/daneel/ClassTransformerTest.java Mon Mar 07 20:38:30 2011 +0100 @@ -0,0 +1,117 @@ +/* + * Daneel - Dalvik to Java bytecode compiler + * Copyright (C) 2011 IcedRobot team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package org.icedrobot.daneel; + +import static org.junit.Assert.assertEquals; + +import org.icedrobot.daneel.dex.ClassDef; +import org.icedrobot.daneel.dex.DexFile; +import org.junit.Test; + +public class ClassTransformerTest extends ClassLoader { + + @Test + public void testTransformClassStaticFields() throws Exception { + ClassDef classDef = DexFile.parse(TEST_STATIC_FIELDS).getClassDef(0); + byte[] bytecode = ClassTransformer.transformClass(classDef); + Class<?> cls = defineClass(null, bytecode, 0, bytecode.length); + assertEquals(7, cls.getFields().length); + assertEquals("public static byte StaticFieldTest.staticByte", + cls.getField("staticByte").toString()); + assertEquals("public static short StaticFieldTest.staticShort", + cls.getField("staticShort").toString()); + assertEquals("public static int StaticFieldTest.staticInt", + cls.getField("staticInt").toString()); + assertEquals("public static long StaticFieldTest.staticLong", + cls.getField("staticLong").toString()); + assertEquals("public static float StaticFieldTest.staticFloat", + cls.getField("staticFloat").toString()); + assertEquals("public static double StaticFieldTest.staticDouble", + cls.getField("staticDouble").toString()); + assertEquals("public static java.lang.Object StaticFieldTest.staticObject", + cls.getField("staticObject").toString()); + } + + /** + * Test data containing the DEX file derived from the following source: + * <code><pre>public class StaticFieldTest { + * public static byte staticByte; + * public static short staticShort; + * public static int staticInt; + * public static long staticLong; + * public static float staticFloat; + * public static double staticDouble; + * public static Object staticObject; + * }</pre></code> + */ + private static final byte[] TEST_STATIC_FIELDS = new byte[] { + 100, 101, 120, 10, 48, 51, 53, 0, 38, 92, -84, -49, 38, 96, + 19, 12, -90,-123, -66, 81, 46, -61,-122, -38, 56, 23, 99, -23, + 42, -14, -71, -28, -52, 2, 0, 0, 112, 0, 0, 0, 120, 86, + 52, 18, 0, 0, 0, 0, 0, 0, 0, 0, 56, 2, 0, 0, + 18, 0, 0, 0, 112, 0, 0, 0, 9, 0, 0, 0, -72, 0, + 0, 0, 1, 0, 0, 0, -36, 0, 0, 0, 7, 0, 0, 0, + -24, 0, 0, 0, 2, 0, 0, 0, 32, 1, 0, 0, 1, 0, + 0, 0, 48, 1, 0, 0, 124, 1, 0, 0, 80, 1, 0, 0, + 104, 1, 0, 0, 112, 1, 0, 0, 115, 1, 0, 0, 118, 1, + 0, 0, 121, 1, 0, 0, 124, 1, 0, 0, 127, 1, 0, 0, + -110, 1, 0, 0, -90, 1, 0, 0, -87, 1, 0, 0, -65, 1, + 0, 0, -62, 1, 0, 0, -50, 1, 0, 0, -36, 1, 0, 0, + -23, 1, 0, 0, -12, 1, 0, 0, 0, 2, 0, 0, 14, 2, + 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, + 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 0, + 0, 0, 8, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, + 8, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 11, 0, + 0, 0, 5, 0, 1, 0, 12, 0, 0, 0, 5, 0, 2, 0, + 13, 0, 0, 0, 5, 0, 3, 0, 14, 0, 0, 0, 5, 0, + 4, 0, 15, 0, 0, 0, 5, 0, 6, 0, 16, 0, 0, 0, + 5, 0, 7, 0, 17, 0, 0, 0, 5, 0, 0, 0, 0, 0, + 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, + 1, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 9, 0, + 0, 0, 0, 0, 0, 0, 32, 2, 0, 0, 0, 0, 0, 0, + 1, 0, 1, 0, 1, 0, 0, 0, 27, 2, 0, 0, 4, 0, + 0, 0, 112, 16, 1, 0, 0, 0, 14, 0, 6, 60, 105, 110, + 105, 116, 62, 0, 1, 66, 0, 1, 68, 0, 1, 70, 0, 1, + 73, 0, 1, 74, 0, 17, 76, 83, 116, 97, 116, 105, 99, 70, + 105, 101, 108, 100, 84, 101, 115, 116, 59, 0, 18, 76, 106, 97, + 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, + 59, 0, 1, 83, 0, 20, 83, 116, 97, 116, 105, 99, 70, 105, + 101, 108, 100, 84, 101, 115, 116, 46, 106, 97, 118, 97, 0, 1, + 86, 0, 10, 115, 116, 97, 116, 105, 99, 66, 121, 116, 101, 0, + 12, 115, 116, 97, 116, 105, 99, 68, 111, 117, 98, 108, 101, 0, + 11, 115, 116, 97, 116, 105, 99, 70, 108, 111, 97, 116, 0, 9, + 115, 116, 97, 116, 105, 99, 73, 110, 116, 0, 10, 115, 116, 97, + 116, 105, 99, 76, 111, 110, 103, 0, 12, 115, 116, 97, 116, 105, + 99, 79, 98, 106, 101, 99, 116, 0, 11, 115, 116, 97, 116, 105, + 99, 83, 104, 111, 114, 116, 0, 1, 0, 7, 14, 0, 7, 0, + 1, 0, 0, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, + 1, 9, 0,-127,-128, 4, -48, 2, 12, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 18, 0, 0, 0, 112, 0, 0, 0, 2, 0, 0, 0, 9, 0, + 0, 0, -72, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, + -36, 0, 0, 0, 4, 0, 0, 0, 7, 0, 0, 0, -24, 0, + 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 32, 1, 0, 0, + 6, 0, 0, 0, 1, 0, 0, 0, 48, 1, 0, 0, 1, 32, + 0, 0, 1, 0, 0, 0, 80, 1, 0, 0, 2, 32, 0, 0, + 18, 0, 0, 0, 104, 1, 0, 0, 3, 32, 0, 0, 1, 0, + 0, 0, 27, 2, 0, 0, 0, 32, 0, 0, 1, 0, 0, 0, + 32, 2, 0, 0, 0, 16, 0, 0, 1, 0, 0, 0, 56, 2, + 0, 0 + }; +}