From b6956dfde5305ed9a87478b3f0ebb17b82103fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 17 Jul 2016 07:57:42 +0200 Subject: [PATCH 01/20] AMF3 reading, toString AMF3 writing stub TODO: Long strings, Externizable, finish writing --- .../flash/amf/amf3/AMF3InputStream.java | 477 ++++++++++++++++++ .../flash/amf/amf3/AMF3OutputStream.java | 79 +++ .../decompiler/flash/amf/amf3/AMF3Tools.java | 241 +++++++++ .../decompiler/flash/amf/amf3/Marker.java | 23 + .../amf/amf3/ObjectTypeSerializeHandler.java | 12 + .../jpexs/decompiler/flash/amf/amf3/Pair.java | 20 + .../decompiler/flash/amf/amf3/Traits.java | 41 ++ .../flash/amf/amf3/WithSubValues.java | 8 + 8 files changed, 901 insertions(+) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3OutputStream.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Marker.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ObjectTypeSerializeHandler.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Pair.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Traits.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/WithSubValues.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java new file mode 100644 index 000000000..368232a91 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java @@ -0,0 +1,477 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +import com.jpexs.decompiler.flash.amf.amf3.types.ArrayType; +import com.jpexs.decompiler.flash.amf.amf3.types.XmlType; +import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; +import com.jpexs.decompiler.flash.amf.amf3.types.XmlDocType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorObjectType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorIntType; +import com.jpexs.decompiler.flash.amf.amf3.types.ByteArrayType; +import com.jpexs.decompiler.flash.amf.amf3.types.DateType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorDoubleType; +import com.jpexs.decompiler.flash.amf.amf3.types.DictionaryType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorUIntType; +import com.jpexs.decompiler.flash.amf.amf3.types.BasicType; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class AMF3InputStream extends InputStream { + + public final static Logger LOGGER = Logger.getLogger(AMF3InputStream.class.getName()); + private final InputStream is; + + public AMF3InputStream(InputStream is) { + this.is = is; + } + + public int readU8() throws IOException { + return safeRead(); + } + + public int readU16() throws IOException { + int b1 = safeRead(); + int b2 = safeRead(); + return (b1 << 8) + b2; + } + + public long readU32() throws IOException { + int b1 = safeRead(); + int b2 = safeRead(); + int b3 = safeRead(); + int b4 = safeRead(); + + return (b1 << 24) + (b2 << 16) + (b3 << 8) + b4; + } + + private long readLong() throws IOException { + byte[] readBuffer = new byte[8]; + for (int i = 0; i < 8; i++) { + readBuffer[i] = (byte) safeRead(); + } + return (((long) readBuffer[0] << 56) + + ((long) (readBuffer[1] & 0xff) << 48) + + ((long) (readBuffer[2] & 0xff) << 40) + + ((long) (readBuffer[3] & 0xff) << 32) + + ((long) (readBuffer[4] & 0xff) << 24) + + ((readBuffer[5] & 0xff) << 16) + + ((readBuffer[6] & 0xff) << 8) + + ((readBuffer[7] & 0xff))); + } + + public double readDouble() throws IOException { + long lval = readLong(); + double ret = Double.longBitsToDouble(lval); + return ret; + } + + public long readU29() throws IOException { + long val = 0; + for (int i = 1; i <= 4; i++) { + int b = safeRead(); + if (i == 4) { + val = ((val << 8) + b); + } else { + val = (val << 7) + (b & 0x7F); + if ((b & 0x80) != 0x80) { + break; + } + } + } + return val; + } + + private long signExtend(long val, int size) { + if (((val >> (size - 1)) & 1) == 1) { //has sign bit + long mask = (1 << size) - 1; // 111111...up to size + long positiveVal = (~(val - 1)) & mask; + long negativeVal = -positiveVal; + return negativeVal; + } + return val; + } + + private String readUtf8Char(int byteLength) throws IOException { + if (byteLength == 0) { + return ""; + } + byte buf[] = new byte[(int) byteLength]; //how about long strings(?), will the int length be enough? + int cnt = is.read(buf); + if (cnt < buf.length) { + throw new IOException("EOF"); + } + String retString = new String(buf, "UTF-8"); + return retString; + } + + public String readUtf8Vr(List stringTable) throws IOException { + long u = readU29(); + int stringNoRefFlag = (int) (u & 1); + if (stringNoRefFlag == 1) { + int byteLength = (int) (u >> 1); //TODO: long strings, int is not enough for them + String retString = readUtf8Char(byteLength); + stringTable.add(retString); + LOGGER.log(Level.FINE, "Read string: \"{0}\"", retString); + return retString; + } else { //flag==0 + int stringRefTableIndex = (int) (u >> 1); + + String retString = stringTable.get(stringRefTableIndex); + LOGGER.log(Level.FINE, "Read string: reference({0}):" + retString, stringRefTableIndex); + return retString; + + } + } + + private int safeRead() throws IOException { + int ret = read(); + if (ret == -1) { + throw new IOException("EOF"); + } + return ret; + } + + @Override + public int read() throws IOException { + return is.read(); + } + + public Object readValue() throws IOException { + return readValue(new HashMap<>()); + } + + public Object readValue(Map serializers) throws IOException { + return readValue(serializers, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + } + + private Object readValue(Map serializers, + List objectTable, + List traitsTable, + List stringTable + ) throws IOException { + + int marker = readU8(); + switch (marker) { + case Marker.UNDEFINED: + LOGGER.log(Level.FINE, "Read value: undefined"); + return BasicType.UNDEFINED; + case Marker.NULL: + LOGGER.log(Level.FINE, "Read value: null"); + return BasicType.NULL; + case Marker.FALSE: + LOGGER.log(Level.FINE, "Read value: false"); + return Boolean.FALSE; + case Marker.TRUE: + LOGGER.log(Level.FINE, "Read value: true"); + return Boolean.TRUE; + case Marker.INTEGER: + LOGGER.log(Level.FINE, "Read value: integer"); + long ival = signExtend(readU29(), 29); + LOGGER.log(Level.FINER, "Integer value: {0}", ival); + return ival; + case Marker.DOUBLE: + LOGGER.log(Level.FINE, "Read value: double"); + double dval = readDouble(); + LOGGER.log(Level.FINER, "Double value: {0}", "" + dval); + return dval; + case Marker.STRING: + LOGGER.log(Level.FINE, "Read value: string"); + String sval = readUtf8Vr(stringTable); + LOGGER.log(Level.FINER, "String value: {0}", sval); + return sval; + case Marker.XML_DOC: + LOGGER.log(Level.FINE, "Read value: xml_doc"); + long xmlDocU29 = readU29(); + int xmlDocNoRefFlag = (int) (xmlDocU29 & 1); + if (xmlDocNoRefFlag == 1) { + long byteLength = xmlDocU29 >> 1; + String xval; + if (xmlDocU29 == 0) { + xval = ""; + } else { + byte buf[] = new byte[(int) byteLength]; //TODO: long strings, int is not enough for them + int cnt = is.read(buf); + if (cnt < buf.length) { + throw new IOException("EOF"); + } + xval = new String(buf, "UTF-8"); + } + LOGGER.log(Level.FINER, "XmlDoc value: {0}", xval); + XmlDocType retXmlDoc = new XmlDocType(xval); + objectTable.add(retXmlDoc); + return retXmlDoc; + } else { + int refIndexXmlDoc = (int) (xmlDocU29 >> 1); + LOGGER.log(Level.FINER, "XmlDoc value: reference({0})", refIndexXmlDoc); + return objectTable.get(refIndexXmlDoc); //What if it's not XmlRef? + } + case Marker.DATE: + LOGGER.log(Level.FINE, "Read value: date"); + long dateU29 = readU29(); + int dateNoRefFlag = (int) (dateU29 & 1); + if (dateNoRefFlag == 1) { + //remaining bits of dateU29 are not used + double dtval = readDouble(); + DateType retDate = new DateType(dtval); + LOGGER.log(Level.FINER, "Date value: {0}", retDate); + objectTable.add(retDate); + return retDate; + } else { + int refIndexDate = (int) (dateU29 >> 1); + LOGGER.log(Level.FINER, "Date value: reference({0})", refIndexDate); + return objectTable.get(refIndexDate); //What if it's not Date? + } + case Marker.ARRAY: + LOGGER.log(Level.FINE, "Read value: array"); + long arrayU29 = readU29(); + int arrayNoRefFlag = (int) (arrayU29 & 1); + if (arrayNoRefFlag == 1) { + int denseCount = (int) (arrayU29 >> 1); + LOGGER.log(Level.FINEST, "Array value: denseCount={0}", new Object[]{denseCount}); + List> assocPart = new ArrayList<>(); + List densePart = new ArrayList<>(); + ArrayType retArray = new ArrayType(densePart, assocPart); + objectTable.add(retArray); //add before processing elements which may reference this + + while (true) { + String key = readUtf8Vr(stringTable); + if (key.isEmpty()) { + break; + } else { + Object val = readValue(serializers, objectTable, traitsTable, stringTable); + assocPart.add(new Pair<>(key, val)); + } + } + LOGGER.log(Level.FINEST, "Array value: assocSize={0}", new Object[]{assocPart.size()}); + + for (int i = 0; i < denseCount; i++) { + densePart.add(readValue(serializers, objectTable, traitsTable, stringTable)); + } + LOGGER.log(Level.FINER, "Array value: dense_size={0},assocSize={1}", new Object[]{densePart.size(), assocPart.size()}); + return retArray; + + } else { + int refIndexArray = (int) (arrayU29 >> 1); + LOGGER.log(Level.FINER, "Array value: reference({0})", refIndexArray); + return objectTable.get(refIndexArray); //What if it's not Array? + } + + case Marker.OBJECT: + LOGGER.log(Level.FINE, "Read value: object"); + long objectU29 = readU29(); + int objectNoRefFlag = (int) (objectU29 & 1); + if (objectNoRefFlag == 1) { + Traits traits; + int objectTraitsNoRefFlag = (int) ((objectU29 >> 1) & 1); + if (objectTraitsNoRefFlag == 1) { + int objectTraitsExtFlag = (int) ((objectU29 >> 2) & 1); + ObjectType retObjectType; + if (objectTraitsExtFlag == 1) { + String className = readUtf8Vr(stringTable); + if (!serializers.containsKey(className)) { + throw new IOException("No (de)serializer for class " + className + " found"); + } + retObjectType = serializers.get(className).readObject(className, is); + LOGGER.log(Level.FINER, "Object/Traits value: customSerialized"); + traits = new Traits(className, true, new ArrayList<>());// FIXME??? + traitsTable.add(traits); + //TODO: verify this + } else { + int dynamicFlag = (int) ((objectU29 >> 3) & 1); + int numSealed = (int) (objectU29 >> 4); + LOGGER.log(Level.FINEST, "object dynamicFlag:{0}", dynamicFlag); + LOGGER.log(Level.FINEST, "object numSealed:{0}", numSealed); + String className = readUtf8Vr(stringTable); + LOGGER.log(Level.FINEST, "object className:{0}", className); + List sealedMemberNames = new ArrayList<>(); + for (int i = 0; i < numSealed; i++) { + sealedMemberNames.add(readUtf8Vr(stringTable)); + } + traits = new Traits(className, dynamicFlag == 1, sealedMemberNames); + } + + } else { + int refIndexTraits = (int) (objectU29 >> 2); + traits = traitsTable.get(refIndexTraits); + LOGGER.log(Level.FINER, "Traits value: reference({0}) - traitsize={1}", new Object[]{refIndexTraits, traits.getSealedMemberNames().size()}); + } + + if (objectTraitsNoRefFlag == 1) { + traitsTable.add(traits); + } + List> sealedMembers = new ArrayList<>(); + List> dynamicMembers = new ArrayList<>(); + + Object retObjectType = new ObjectType(traits.isDynamic(), sealedMembers, dynamicMembers, traits.getClassName()); + objectTable.add(retObjectType); //add it before any subvalue can reference it + List sealedMemberValues = new ArrayList<>(); + for (int i = 0; i < traits.getSealedMemberNames().size(); i++) { + sealedMemberValues.add(readValue(serializers, objectTable, traitsTable, stringTable)); + } + + for (int i = 0; i < traits.getSealedMemberNames().size(); i++) { + sealedMembers.add(new Pair<>(traits.getSealedMemberNames().get(i), sealedMemberValues.get(i))); + } + if (traits.isDynamic()) { + String dynamicMemberName; + while (!(dynamicMemberName = readUtf8Vr(stringTable)).isEmpty()) { + Object dynamicMemberValue = readValue(serializers, objectTable, traitsTable, stringTable); + dynamicMembers.add(new Pair<>(dynamicMemberName, dynamicMemberValue)); + } + } + + LOGGER.log(Level.FINER, "Object value: dynamic={0},className={1},sealedSize={2},dynamicSize={3}", new Object[]{traits.isDynamic(), traits.getClassName(), sealedMembers.size(), dynamicMembers.size()}); + return retObjectType; + } else { + int refIndexObject = (int) (objectU29 >> 1); + LOGGER.log(Level.FINER, "Object value: reference({0})", refIndexObject); + return objectTable.get(refIndexObject); + } + case Marker.XML: + LOGGER.log(Level.FINE, "Read value: xml"); + long xmlU29 = readU29(); + int xmlNoRefFlag = (int) (xmlU29 & 1); + if (xmlNoRefFlag == 1) { + int byteLength = (int) (xmlU29 >> 1); //TODO: long strings, int is not enough for them + String xString = readUtf8Char(byteLength); + XmlType retXmlType = new XmlType(xString); + LOGGER.log(Level.FINER, "Xml value: {0}", xString); + objectTable.add(retXmlType); + return retXmlType; + } else { + int refIndexXml = (int) (xmlU29 >> 1); + LOGGER.log(Level.FINER, "XML value: reference({0})", refIndexXml); + return objectTable.get(refIndexXml); + } + case Marker.BYTE_ARRAY: + LOGGER.log(Level.FINE, "Read value: bytearray"); + long byteArrayU29 = readU29(); + int byteArrayNoRefFlag = (int) (byteArrayU29 & 1); + if (byteArrayNoRefFlag == 1) { + int byteArrayLength = (int) (byteArrayU29 >> 1); + byte byteArrayBuf[] = new byte[byteArrayLength]; + if (is.read(byteArrayBuf) != byteArrayLength) { + throw new IOException("EOF"); + } + + LOGGER.log(Level.FINER, "ByteArray value: bytes[{0}]", byteArrayLength); + ByteArrayType retByteArrayType = new ByteArrayType(byteArrayBuf); + objectTable.add(retByteArrayType); + return retByteArrayType; + } else { + int refIndexByteArray = (int) (byteArrayU29 >> 1); + LOGGER.log(Level.FINER, "ByteArray value: reference({0})", refIndexByteArray); + return objectTable.get(refIndexByteArray); + } + case Marker.VECTOR_INT: + LOGGER.log(Level.FINE, "Read value: vector_int"); + long vectorIntU29 = readU29(); + int vectorIntNoRefFlag = (int) (vectorIntU29 & 1); + if (vectorIntNoRefFlag == 1) { + int vectorIntCountItems = (int) (vectorIntU29 >> 1); + int fixed = readU8(); + List vals = new ArrayList<>(); + for (int i = 0; i < vectorIntCountItems; i++) { + vals.add(readU32()); + } + VectorIntType retVectorInt = new VectorIntType(fixed == 1, vals); + LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}]", new Object[]{fixed, vectorIntCountItems}); + objectTable.add(retVectorInt); + return retVectorInt; + } else { + int refIndexVectorInt = (int) (vectorIntU29 >> 1); + LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorInt); + return objectTable.get(refIndexVectorInt); + } + case Marker.VECTOR_UINT: + LOGGER.log(Level.FINE, "Read value: vector_uint"); + long vectorUIntU29 = readU29(); + int vectorUIntNoRefFlag = (int) (vectorUIntU29 & 1); + if (vectorUIntNoRefFlag == 1) { + int vectorUIntCountItems = (int) (vectorUIntU29 >> 1); + int fixed = readU8(); + List vals = new ArrayList<>(); + for (int i = 0; i < vectorUIntCountItems; i++) { + vals.add(signExtend(readU32(), 32)); + } + VectorUIntType retVectorUInt = new VectorUIntType(fixed == 1, vals); + LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}]", new Object[]{fixed, vectorUIntCountItems}); + objectTable.add(retVectorUInt); + return retVectorUInt; + } else { + int refIndexVectorUInt = (int) (vectorUIntU29 >> 1); + LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorUInt); + return objectTable.get(refIndexVectorUInt); + } + case Marker.VECTOR_DOUBLE: + LOGGER.log(Level.FINE, "Read value: vector_double"); + long vectorDoubleU29 = readU29(); + int vectorDoubleNoRefFlag = (int) (vectorDoubleU29 & 1); + if (vectorDoubleNoRefFlag == 1) { + int vectorDoubleCountItems = (int) (vectorDoubleU29 >> 1); + int fixed = readU8(); + List vals = new ArrayList<>(); + for (int i = 0; i < vectorDoubleCountItems; i++) { + vals.add(readDouble()); + } + VectorDoubleType retVectorDouble = new VectorDoubleType(fixed == 1, vals); + LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}]", new Object[]{fixed, vectorDoubleCountItems}); + objectTable.add(retVectorDouble); + return retVectorDouble; + } else { + int refIndexVectorDouble = (int) (vectorDoubleU29 >> 1); + LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorDouble); + return objectTable.get(refIndexVectorDouble); + } + case Marker.VECTOR_OBJECT: + LOGGER.log(Level.FINE, "Read value: vector_object"); + long vectorObjectU29 = readU29(); + int vectorObjectNoRefFlag = (int) (vectorObjectU29 & 1); + if (vectorObjectNoRefFlag == 1) { + int vectorObjectCountItems = (int) (vectorObjectU29 >> 1); + int fixed = readU8(); + String objectTypeName = readUtf8Vr(stringTable); //uses "*" for any type + List vals = new ArrayList<>(); + for (int i = 0; i < vectorObjectCountItems; i++) { + vals.add(readValue(serializers, objectTable, traitsTable, stringTable)); + } + VectorObjectType retVectorObject = new VectorObjectType(fixed == 1, objectTypeName, vals); + LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}, typeName:{2}]", new Object[]{fixed, vectorObjectCountItems, objectTypeName}); + objectTable.add(retVectorObject); + return retVectorObject; + } else { + int refIndexVectorObject = (int) (vectorObjectU29 >> 1); + LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorObject); + return objectTable.get(refIndexVectorObject); + } + case Marker.DICTIONARY: + long dictionaryObjectU29 = readU29(); + int dictionaryNoRefFlag = (int) (dictionaryObjectU29 & 1); + if (dictionaryNoRefFlag == 1) { + int numEntries = (int) (dictionaryObjectU29 >> 1); + int weakKeys = readU8(); + List> data = new ArrayList<>(); + for (int i = 0; i < numEntries; i++) { + Object key = readValue(serializers, objectTable, traitsTable, stringTable); + Object val = readValue(serializers, objectTable, traitsTable, stringTable); + data.add(new Pair<>(key, val)); + } + DictionaryType retDictionary = new DictionaryType(weakKeys == 1, data); + objectTable.add(retDictionary); + return retDictionary; + } else { + int refIndexDictionary = (int) (dictionaryObjectU29 >> 1); + LOGGER.log(Level.FINER, "Dictionary value: reference({0})", refIndexDictionary); + return objectTable.get(refIndexDictionary); + } + default: + return null; + } + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3OutputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3OutputStream.java new file mode 100644 index 000000000..0c93ce193 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3OutputStream.java @@ -0,0 +1,79 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +import java.io.IOException; +import java.io.OutputStream; + +public class AMF3OutputStream extends OutputStream { + + private final OutputStream os; + + public AMF3OutputStream(OutputStream os) { + this.os = os; + } + + public void writeUI8(int v) throws IOException { + write(v); + } + + public void writeUI16(int v) throws IOException { + int b1 = (v >> 8) & 0xff; + int b2 = v & 0xff; + write(b1); + write(b2); + } + + public void writeUI32(long v) throws IOException { + int b1 = (int) ((v >> 24) & 0xff); + int b2 = (int) ((v >> 16) & 0xff); + int b3 = (int) ((v >> 8) & 0xff); + int b4 = (int) (v & 0xff); + + write(b1); + write(b2); + write(b3); + write(b4); + } + + public void writeDouble(double v) throws IOException { + writeUI32(Double.doubleToLongBits(v)); + } + + public void writeUI29(long v) throws IOException { + if (v <= 0x7F) { + write((int) v); + } else if (v <= 0x3FFF) { + int b1 = (int) ((v >> 7) & 0x7F); + int b2 = (int) (v & 0x7F); + write(b1); + write(b2); + } else if (v <= 0x1FFFFF) { + int b1 = (int) ((v >> 14) & 0x7F); + int b2 = (int) ((v >> 7) & 0x7F); + int b3 = (int) (v & 0x7F); + write(b1); + write(b2); + write(b3); + } else if (v <= 0x3FFFFFFF) { + int b1 = (int) ((v >> 21) & 0x7F); + int b2 = (int) ((v >> 14) & 0x7F); + int b3 = (int) ((v >> 7) & 0x7F); + int b4 = (int) (v & 0x7F); + write(b1); + write(b2); + write(b3); + write(b4); + } else { + throw new IllegalArgumentException("Value too long"); + } + } + + @Override + public void write(int v) throws IOException { + os.write(v); + } + + public void writeValue(Object object) throws IOException { + //TODO:!!! + throw new UnsupportedOperationException("Not implemented yet"); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java new file mode 100644 index 000000000..1bf97de9a --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java @@ -0,0 +1,241 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +import com.jpexs.decompiler.flash.amf.amf3.types.ArrayType; +import com.jpexs.decompiler.flash.amf.amf3.types.XmlType; +import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; +import com.jpexs.decompiler.flash.amf.amf3.types.AbstractVectorType; +import com.jpexs.decompiler.flash.amf.amf3.types.XmlDocType; +import com.jpexs.decompiler.flash.amf.amf3.types.ByteArrayType; +import com.jpexs.decompiler.flash.amf.amf3.types.DateType; +import com.jpexs.decompiler.flash.amf.amf3.types.DictionaryType; +import com.jpexs.decompiler.flash.amf.amf3.types.BasicType; +import com.jpexs.helpers.Helper; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class AMF3Tools { + + /** + * Converts AMF value to something human-readable. + * + * @param obj + * @return + */ + public static String amfToString(Object obj) { + Map refCount = new HashMap<>(); + List objectList = new ArrayList<>(); + Map objectAlias = new HashMap<>(); + populateObjects(obj, refCount, objectList, objectAlias); + return amfToString(new ArrayList<>(), 0, obj, refCount, objectAlias); + } + + /** + * Populates all object instances and their references and generates aliases + * + * @param object Object to be populated + * @param referenceCount Result: Map of reference number + * @param objectList Result: List of all found object instances + * @param objectAlias Result: Map of assigned object names + */ + public static void populateObjects(Object object, Map referenceCount, List objectList, Map objectAlias) { + if (((List) Arrays.asList(String.class, Long.class, Double.class, BasicType.class, Boolean.class)).contains(object.getClass())) { + return; + } + int prevRef = 0; + if (referenceCount.containsKey(object)) { + prevRef = referenceCount.get(object); + } + referenceCount.put(object, prevRef + 1); + if (prevRef == 0) { + if (object instanceof WithSubValues) { + List subvalues = ((WithSubValues) object).getSubValues(); + for (Object o : subvalues) { + populateObjects(o, referenceCount, objectList, objectAlias); + } + } + objectList.add(object); + objectAlias.put(object, "obj" + objectList.size()); + } + } + + private static String indent(int level) { + String na = ""; + for (int i = 0; i < level; i++) { + na += " "; + } + return na; + } + + /** + * Processes one level of object and converts it to string + * + * @param processedObjects + * @param level + * @param object + * @param referenceCount + * @param objectAlias + * @return + */ + private static String amfToString(List processedObjects, int level, Object object, Map referenceCount, Map objectAlias) { + if (object instanceof String) { + return "\"" + Helper.escapeActionScriptString((String) object) + "\""; + } + if (((List) Arrays.asList(Long.class, Double.class, BasicType.class, Boolean.class)).contains(object.getClass())) { + return object.toString(); + } + + StringBuilder ret = new StringBuilder(); + + Integer refCount = referenceCount.get(object); + if (refCount > 1 && processedObjects.contains(object)) { + ret.append("#").append(objectAlias.get(object)); + return ret.toString(); + } + processedObjects.add(object); + + String addId = refCount > 1 ? indent(level + 1) + "\"id\": \"" + objectAlias.get(object) + "\"\r\n" : ""; + + if (object instanceof AbstractVectorType) { + AbstractVectorType avt = (AbstractVectorType) object; + ret.append("{\r\n"); + ret.append(indent(level + 1)).append("\"type\": \"Vector\",\r\n"); + ret.append(addId); + ret.append(indent(level + 1)).append("\"fixed\": ").append(avt.isFixed()).append(",\r\n"); + ret.append(indent(level + 1)).append("\"subtype\": ").append(amfToString(processedObjects, level, avt.getTypeName(), referenceCount, objectAlias)).append(",\r\n"); + ret.append(indent(level + 1)).append("\"values\": ["); + for (int i = 0; i < avt.getValues().size(); i++) { + if (i > 0) { + ret.append(", "); + } + ret.append(amfToString(processedObjects, level + 1, avt.getValues().get(i), referenceCount, objectAlias)); + } + ret.append("]\r\n"); + ret.append(indent(level)).append("}"); + } else if (object instanceof ObjectType) { + ObjectType ot = (ObjectType) object; + ret.append("{\r\n"); + ret.append(indent(level + 1) + "\"type\": \"Object\",\r\n"); + ret.append(addId); + ret.append(indent(level + 1) + "\"className\": " + amfToString(processedObjects, level, ot.getClassName(), referenceCount, objectAlias) + ",\r\n"); + ret.append(indent(level + 1) + "\"dynamic\": " + ot.isDynamic() + ",\r\n"); + if (!ot.getSealedMembers().isEmpty()) { + ret.append(indent(level + 1) + "\"sealedMembers\": {\r\n"); + for (int i = 0; i < ot.getSealedMembers().size(); i++) { + Pair member = ot.getSealedMembers().get(i); + ret.append(indent(level + 2) + amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias) + ":" + amfToString(processedObjects, level + 1, member.getSecond(), referenceCount, objectAlias)); + if (i < ot.getSealedMembers().size() - 1) { + ret.append(",\r\n"); + } else { + ret.append("\r\n"); + } + } + ret.append(indent(level + 1) + "}"); + if (!ot.getDynamicMembers().isEmpty()) { + ret.append(","); + } + ret.append("\r\n"); + } + if (!ot.getDynamicMembers().isEmpty()) { + ret.append(indent(level + 1) + "\"dynamicMembers\": {\r\n"); + for (int i = 0; i < ot.getDynamicMembers().size(); i++) { + Pair member = ot.getDynamicMembers().get(i); + ret.append(indent(level + 2) + amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias) + ":" + amfToString(processedObjects, level + 2, member.getSecond(), referenceCount, objectAlias)); + if (i < ot.getDynamicMembers().size() - 1) { + ret.append(","); + } + ret.append("\r\n"); + } + ret.append(indent(level + 1) + "}\r\n"); + } + ret.append(indent(level) + "}"); + } else if (object instanceof ArrayType) { + ArrayType at = (ArrayType) object; + ret.append("{\r\n"); + ret.append(indent(level + 1) + "\"type\": \"Array\",\r\n"); + ret.append(indent(level + 1) + "\"denseValues\": ["); + + for (int i = 0; i < at.getDenseValues().size(); i++) { + if (i > 0) { + ret.append(", "); + } + ret.append(amfToString(processedObjects, level + 2, at.getDenseValues().get(i), referenceCount, objectAlias)); + } + ret.append("],\r\n"); + ret.append(indent(level + 1) + "\"associativeValues\": ["); + if (!at.getAssociativeValues().isEmpty()) { + ret.append("\r\n"); + } + for (int i = 0; i < at.getAssociativeValues().size(); i++) { + Pair p = at.getAssociativeValues().get(i); + ret.append(indent(level + 2) + amfToString(processedObjects, level + 1, p.getFirst(), referenceCount, objectAlias) + " : " + amfToString(processedObjects, level + 1, p.getSecond(), referenceCount, objectAlias)); + if (i < at.getAssociativeValues().size() - 1) { + ret.append(","); + } + ret.append("\r\n"); + } + if (!at.getAssociativeValues().isEmpty()) { + ret.append(indent(level + 1)); + } + ret.append("]\r\n"); + ret.append(indent(level) + "}"); + } else if (object instanceof DictionaryType) { + DictionaryType dt = (DictionaryType) object; + ret.append("{\r\n"); + ret.append(indent(level + 1) + "\"type\": \"Dictionary\",\r\n"); + ret.append(addId); + ret.append(indent(level + 1) + "\"value\": {\r\n"); + for (int i = 0; i < dt.getPairs().size(); i++) { + Pair pair = dt.getPairs().get(i); + ret.append(indent(level + 1) + amfToString(processedObjects, level + 1, pair.getFirst(), referenceCount, objectAlias) + " : " + amfToString(processedObjects, level + 1, pair.getSecond(), referenceCount, objectAlias)); + if (i < dt.getPairs().size() - 1) { + ret.append(","); + } + ret.append("\r\n"); + } + ret.append(indent(level + 1) + "}\r\n"); + ret.append(indent(level) + "}"); + } else if (object instanceof ByteArrayType) { + ByteArrayType ba = (ByteArrayType) object; + byte data[] = ba.getData(); + return "{\r\n" + + indent(level + 1) + "\"type\": \"ByteArray\",\r\n" + + addId + + indent(level + 1) + "\"value\": 0x" + javax.xml.bind.DatatypeConverter.printHexBinary(data) + "\r\n" + + indent(level) + "}"; + } else if (object instanceof DateType) { + DateType dt = (DateType) object; + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS"); + return "{\r\n" + + indent(level + 1) + "\"type\": \"Date\",\r\n" + + addId + + indent(level + 1) + "\"value\": " + amfToString(processedObjects, level, sdf.format(new Date((long) dt.getVal())), referenceCount, objectAlias) + "\r\n" + + indent(level) + "}"; + + } else if (object instanceof XmlDocType) { + return "{\r\n" + + indent(level + 1) + "\"type\": \"XML\",\r\n" + + addId + + indent(level + 1) + "\"value\": " + amfToString(processedObjects, level, ((XmlDocType) object).getData(), referenceCount, objectAlias) + "\r\n" + + indent(level) + "}"; + } else if (object instanceof XmlType) { + return "{\r\n" + + indent(level + 1) + "\"type\": \"XMLDocument\",\r\n" + + addId + + indent(level + 1) + "\"value\": " + amfToString(processedObjects, level, ((XmlType) object).getData(), referenceCount, objectAlias) + "\r\n" + + indent(level) + "}"; + } else if (object == BasicType.NULL) { + return "null"; + } else if (object == BasicType.UNDEFINED) { + return "undefined"; + } else { + throw new IllegalArgumentException("Unsupported type: " + object.getClass()); + } + + return ret.toString(); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Marker.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Marker.java new file mode 100644 index 000000000..2ace21d0e --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Marker.java @@ -0,0 +1,23 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +public class Marker { + + public static final int UNDEFINED = 0x00; + public static final int NULL = 0x01; + public static final int FALSE = 0x02; + public static final int TRUE = 0x03; + public static final int INTEGER = 0x04; + public static final int DOUBLE = 0x05; + public static final int STRING = 0x06; + public static final int XML_DOC = 0x07; + public static final int DATE = 0x08; + public static final int ARRAY = 0x09; + public static final int OBJECT = 0x0A; + public static final int XML = 0x0B; + public static final int BYTE_ARRAY = 0x0C; + public static final int VECTOR_INT = 0x0D; + public static final int VECTOR_UINT = 0x0E; + public static final int VECTOR_DOUBLE = 0x0F; + public static final int VECTOR_OBJECT = 0x10; + public static final int DICTIONARY = 0x11; +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ObjectTypeSerializeHandler.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ObjectTypeSerializeHandler.java new file mode 100644 index 000000000..67fe64ac2 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ObjectTypeSerializeHandler.java @@ -0,0 +1,12 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; +import java.io.InputStream; +import java.io.OutputStream; + +public interface ObjectTypeSerializeHandler { + + public ObjectType readObject(String className, InputStream is); + + public void writeObject(ObjectType val, OutputStream os); +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Pair.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Pair.java new file mode 100644 index 000000000..021814ec2 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Pair.java @@ -0,0 +1,20 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +public class Pair { + + private final T1 first; + private final T2 second; + + public Pair(T1 first, T2 second) { + this.first = first; + this.second = second; + } + + public T1 getFirst() { + return first; + } + + public T2 getSecond() { + return second; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Traits.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Traits.java new file mode 100644 index 000000000..2ebd59327 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Traits.java @@ -0,0 +1,41 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +import java.util.List; + +public class Traits { + + private String className; + private boolean dynamic; + private List sealedMemberNames; + + public Traits(String className, boolean dynamic, List sealedMemberNames) { + this.className = className; + this.dynamic = dynamic; + this.sealedMemberNames = sealedMemberNames; + } + + public String getClassName() { + return className; + } + + public boolean isDynamic() { + return dynamic; + } + + public List getSealedMemberNames() { + return sealedMemberNames; + } + + public void setClassName(String className) { + this.className = className; + } + + public void setDynamic(boolean dynamic) { + this.dynamic = dynamic; + } + + public void setSealedMemberNames(List sealedMemberNames) { + this.sealedMemberNames = sealedMemberNames; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/WithSubValues.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/WithSubValues.java new file mode 100644 index 000000000..4baa7973c --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/WithSubValues.java @@ -0,0 +1,8 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +import java.util.List; + +public interface WithSubValues { + + public List getSubValues(); +} From ec5ca5c3f1fcfd28e67a849357367e885fcae793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 17 Jul 2016 08:43:27 +0200 Subject: [PATCH 02/20] append for StringBuilder --- .../decompiler/flash/amf/amf3/AMF3Tools.java | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java index 1bf97de9a..85125208d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java @@ -119,45 +119,45 @@ public class AMF3Tools { } else if (object instanceof ObjectType) { ObjectType ot = (ObjectType) object; ret.append("{\r\n"); - ret.append(indent(level + 1) + "\"type\": \"Object\",\r\n"); + ret.append(indent(level + 1)).append("\"type\": \"Object\",\r\n"); ret.append(addId); - ret.append(indent(level + 1) + "\"className\": " + amfToString(processedObjects, level, ot.getClassName(), referenceCount, objectAlias) + ",\r\n"); - ret.append(indent(level + 1) + "\"dynamic\": " + ot.isDynamic() + ",\r\n"); + ret.append(indent(level + 1)).append("\"className\": ").append(amfToString(processedObjects, level, ot.getClassName(), referenceCount, objectAlias)).append(",\r\n"); + ret.append(indent(level + 1)).append("\"dynamic\": ").append(ot.isDynamic()).append(",\r\n"); if (!ot.getSealedMembers().isEmpty()) { - ret.append(indent(level + 1) + "\"sealedMembers\": {\r\n"); + ret.append(indent(level + 1)).append("\"sealedMembers\": {\r\n"); for (int i = 0; i < ot.getSealedMembers().size(); i++) { Pair member = ot.getSealedMembers().get(i); - ret.append(indent(level + 2) + amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias) + ":" + amfToString(processedObjects, level + 1, member.getSecond(), referenceCount, objectAlias)); + ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 1, member.getSecond(), referenceCount, objectAlias)); if (i < ot.getSealedMembers().size() - 1) { ret.append(",\r\n"); } else { ret.append("\r\n"); } } - ret.append(indent(level + 1) + "}"); + ret.append(indent(level + 1)).append("}"); if (!ot.getDynamicMembers().isEmpty()) { ret.append(","); } ret.append("\r\n"); } if (!ot.getDynamicMembers().isEmpty()) { - ret.append(indent(level + 1) + "\"dynamicMembers\": {\r\n"); + ret.append(indent(level + 1)).append("\"dynamicMembers\": {\r\n"); for (int i = 0; i < ot.getDynamicMembers().size(); i++) { Pair member = ot.getDynamicMembers().get(i); - ret.append(indent(level + 2) + amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias) + ":" + amfToString(processedObjects, level + 2, member.getSecond(), referenceCount, objectAlias)); + ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 2, member.getSecond(), referenceCount, objectAlias)); if (i < ot.getDynamicMembers().size() - 1) { ret.append(","); } ret.append("\r\n"); } - ret.append(indent(level + 1) + "}\r\n"); + ret.append(indent(level + 1)).append("}\r\n"); } - ret.append(indent(level) + "}"); + ret.append(indent(level)).append("}"); } else if (object instanceof ArrayType) { ArrayType at = (ArrayType) object; ret.append("{\r\n"); - ret.append(indent(level + 1) + "\"type\": \"Array\",\r\n"); - ret.append(indent(level + 1) + "\"denseValues\": ["); + ret.append(indent(level + 1)).append("\"type\": \"Array\",\r\n"); + ret.append(indent(level + 1)).append("\"denseValues\": ["); for (int i = 0; i < at.getDenseValues().size(); i++) { if (i > 0) { @@ -166,13 +166,13 @@ public class AMF3Tools { ret.append(amfToString(processedObjects, level + 2, at.getDenseValues().get(i), referenceCount, objectAlias)); } ret.append("],\r\n"); - ret.append(indent(level + 1) + "\"associativeValues\": ["); + ret.append(indent(level + 1)).append("\"associativeValues\": ["); if (!at.getAssociativeValues().isEmpty()) { ret.append("\r\n"); } for (int i = 0; i < at.getAssociativeValues().size(); i++) { Pair p = at.getAssociativeValues().get(i); - ret.append(indent(level + 2) + amfToString(processedObjects, level + 1, p.getFirst(), referenceCount, objectAlias) + " : " + amfToString(processedObjects, level + 1, p.getSecond(), referenceCount, objectAlias)); + ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 1, p.getFirst(), referenceCount, objectAlias)).append(" : ").append(amfToString(processedObjects, level + 1, p.getSecond(), referenceCount, objectAlias)); if (i < at.getAssociativeValues().size() - 1) { ret.append(","); } @@ -182,23 +182,23 @@ public class AMF3Tools { ret.append(indent(level + 1)); } ret.append("]\r\n"); - ret.append(indent(level) + "}"); + ret.append(indent(level)).append("}"); } else if (object instanceof DictionaryType) { DictionaryType dt = (DictionaryType) object; ret.append("{\r\n"); - ret.append(indent(level + 1) + "\"type\": \"Dictionary\",\r\n"); + ret.append(indent(level + 1)).append("\"type\": \"Dictionary\",\r\n"); ret.append(addId); - ret.append(indent(level + 1) + "\"value\": {\r\n"); + ret.append(indent(level + 1)).append("\"value\": {\r\n"); for (int i = 0; i < dt.getPairs().size(); i++) { Pair pair = dt.getPairs().get(i); - ret.append(indent(level + 1) + amfToString(processedObjects, level + 1, pair.getFirst(), referenceCount, objectAlias) + " : " + amfToString(processedObjects, level + 1, pair.getSecond(), referenceCount, objectAlias)); + ret.append(indent(level + 1)).append(amfToString(processedObjects, level + 1, pair.getFirst(), referenceCount, objectAlias)).append(" : ").append(amfToString(processedObjects, level + 1, pair.getSecond(), referenceCount, objectAlias)); if (i < dt.getPairs().size() - 1) { ret.append(","); } ret.append("\r\n"); } - ret.append(indent(level + 1) + "}\r\n"); - ret.append(indent(level) + "}"); + ret.append(indent(level + 1)).append("}\r\n"); + ret.append(indent(level)).append("}"); } else if (object instanceof ByteArrayType) { ByteArrayType ba = (ByteArrayType) object; byte data[] = ba.getData(); From f7b3d502cb4c74ead2b5d6159ceb471975bc378d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 17 Jul 2016 15:48:56 +0200 Subject: [PATCH 03/20] AMF3 tests, exceptions --- .gitignore | 143 +- .../flash/amf/amf3/AMF3InputStream.java | 139 +- .../decompiler/flash/amf/amf3/AMF3Tools.java | 82 +- .../amf/amf3/NoSerializerExistsException.java | 25 + .../amf/amf3/ObjectTypeSerializeHandler.java | 7 +- .../PrematureEndOfTheStreamException.java | 11 + .../flash/amf/amf3/UnsupportedValueType.java | 16 + .../amf/amf3/types/AbstractVectorType.java | 40 + .../flash/amf/amf3/types/ArrayType.java | 46 + .../flash/amf/amf3/types/BasicType.java | 26 + .../flash/amf/amf3/types/ByteArrayType.java | 22 + .../flash/amf/amf3/types/DateType.java | 30 + .../flash/amf/amf3/types/DictionaryType.java | 38 + .../flash/amf/amf3/types/ObjectType.java | 116 + .../amf/amf3/types/VectorDoubleType.java | 16 + .../flash/amf/amf3/types/VectorIntType.java | 16 + .../amf/amf3/types/VectorObjectType.java | 21 + .../flash/amf/amf3/types/VectorUIntType.java | 17 + .../flash/amf/amf3/types/XmlDocType.java | 24 + .../flash/amf/amf3/types/XmlType.java | 24 + .../flash/amf/amf3/Amf3InputStreamTest.java | 171 + libsrc/ffdec_lib/testdata/amf3/AmfTest.as | 169 + libsrc/ffdec_lib/testdata/amf3/AmfTest.swf | Bin 0 -> 2287 bytes libsrc/ffdec_lib/testdata/amf3/CustomClass.as | 32 + .../testdata/amf3/ObjectWithCustom.as | 24 + libsrc/ffdec_lib/testdata/amf3/app.xml | 14 + .../ffdec_lib/testdata/amf3/generated/all.bin | Bin 0 -> 765 bytes .../testdata/amf3/generated/custom.bin | 3 + .../noserializer_array_associative.bin | 2 + .../generated/noserializer_array_dense.bin | 2 + .../generated/noserializer_dictionary_key.bin | Bin 0 -> 68 bytes .../noserializer_dictionary_value.bin | Bin 0 -> 69 bytes .../generated/noserializer_object_dynamic.bin | 3 + .../generated/noserializer_object_sealed.bin | 3 + .../amf3/generated/noserializer_vector.bin | Bin 0 -> 38 bytes libsrc/ffdec_lib/testdata/amf3/run.bat | 20 + libsrc/ffdec_lib/testdata/as3/CustomClass.as | 34 + libsrc/ttf/nbproject/build-impl.xml | 2832 +++++++++-------- libsrc/ttf/nbproject/genfiles.properties | 16 +- 39 files changed, 2639 insertions(+), 1545 deletions(-) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/NoSerializerExistsException.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/UnsupportedValueType.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/BasicType.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorDoubleType.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorIntType.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorObjectType.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorUIntType.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlDocType.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlType.java create mode 100644 libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java create mode 100644 libsrc/ffdec_lib/testdata/amf3/AmfTest.as create mode 100644 libsrc/ffdec_lib/testdata/amf3/AmfTest.swf create mode 100644 libsrc/ffdec_lib/testdata/amf3/CustomClass.as create mode 100644 libsrc/ffdec_lib/testdata/amf3/ObjectWithCustom.as create mode 100644 libsrc/ffdec_lib/testdata/amf3/app.xml create mode 100644 libsrc/ffdec_lib/testdata/amf3/generated/all.bin create mode 100644 libsrc/ffdec_lib/testdata/amf3/generated/custom.bin create mode 100644 libsrc/ffdec_lib/testdata/amf3/generated/noserializer_array_associative.bin create mode 100644 libsrc/ffdec_lib/testdata/amf3/generated/noserializer_array_dense.bin create mode 100644 libsrc/ffdec_lib/testdata/amf3/generated/noserializer_dictionary_key.bin create mode 100644 libsrc/ffdec_lib/testdata/amf3/generated/noserializer_dictionary_value.bin create mode 100644 libsrc/ffdec_lib/testdata/amf3/generated/noserializer_object_dynamic.bin create mode 100644 libsrc/ffdec_lib/testdata/amf3/generated/noserializer_object_sealed.bin create mode 100644 libsrc/ffdec_lib/testdata/amf3/generated/noserializer_vector.bin create mode 100644 libsrc/ffdec_lib/testdata/amf3/run.bat create mode 100644 libsrc/ffdec_lib/testdata/as3/CustomClass.as diff --git a/.gitignore b/.gitignore index 25c90c1d4..d3caca575 100644 --- a/.gitignore +++ b/.gitignore @@ -1,71 +1,72 @@ -*.orig -*.dcu -*.ddp -*.orig.* -*.chg.* -*.rej -*.conflict~ -*.identcache -*.recompiled.swf -run_test_*.swf -Thumbs.db -/build/ -/reports/ -/installer.cfg -/releases/ -/locales/ -/javadoc/ -/coverage/ -/dist/ -/mac/ -/testdata/decompile/ -/testdata/recompile/ -/build_exe.xml -/build_exe64.xml -/coverage.ec -/libsrc/jsyntaxpane/jsyntaxpane/src/target/ -hs_err_pid*.log -*.~* -/revision.txt -/lib/ffdec_lib.jar -/lib/README.TXT -/libsrc/avi/nbproject/private/ -/libsrc/gnujpdf/dist/ -/libsrc/jsyntaxpane/jsyntaxpane/target/ -/libsrc/ffdec_lib/build/ -/libsrc/ffdec_lib/nbproject/private/ -/libsrc/ffdec_lib/javadoc/ -/libsrc/ffdec_lib/coverage/ -/libsrc/ffdec_lib/reports/ -/libsrc/ffdec_lib/dist/ -/libsrc/ffdec_lib/testdata/decompile/ -/libsrc/ffdec_lib/testdata/recompile/ -/libsrc/ffdec_lib/testdata/directediting/ -/libsrc/ffdec_lib/coverage.ec -/libsrc/ffdec_lib/revision.txt -/libsrc/gif/nbproject/private/ -/libsrc/gif/build/ -/libsrc/jpproxy/nbproject/private/ -/libsrc/jpproxy/dist/ -/libsrc/jpproxy/build/ -/libsrc/LZMA/nbproject/private/ -/libsrc/ttf/nbproject/private/ -/libsrc/ttf/build/ -/libsrc/tablelayout/nbproject/private/ -/libsrc/tablelayout/build/ -/libsrc/tablelayout/dist/ -/libsrc/uploader/nbproject/private/ -/libsrc/uploader/build/ -/libsrc/uploader/dist/ -/jpexs_website.properties -/version.properties -/tools.properties -/nbproject/private/ -/libsrc/cmykjpeg/build/ -/libsrc/cmykjpeg/nbproject/private/ -/libsrc/Plugins/nbproject/private/ -/libsrc/Plugins/build/ -/libsrc/Plugins/dist/ -/libsrc/treetable/nbproject/private/ -/libsrc/treetable/build/ -/libsrc/treetable/dist/ \ No newline at end of file +*.orig +*.dcu +*.ddp +*.orig.* +*.chg.* +*.rej +*.conflict~ +*.identcache +*.recompiled.swf +run_test_*.swf +Thumbs.db +/build/ +/reports/ +/installer.cfg +/releases/ +/locales/ +/javadoc/ +/coverage/ +/dist/ +/mac/ +/testdata/decompile/ +/testdata/recompile/ +/build_exe.xml +/build_exe64.xml +/coverage.ec +/libsrc/jsyntaxpane/jsyntaxpane/src/target/ +hs_err_pid*.log +*.~* +/revision.txt +/lib/ffdec_lib.jar +/lib/README.TXT +/libsrc/avi/nbproject/private/ +/libsrc/gnujpdf/dist/ +/libsrc/jsyntaxpane/jsyntaxpane/target/ +/libsrc/ffdec_lib/build/ +/libsrc/ffdec_lib/nbproject/private/ +/libsrc/ffdec_lib/javadoc/ +/libsrc/ffdec_lib/coverage/ +/libsrc/ffdec_lib/reports/ +/libsrc/ffdec_lib/dist/ +/libsrc/ffdec_lib/testdata/decompile/ +/libsrc/ffdec_lib/testdata/recompile/ +/libsrc/ffdec_lib/testdata/directediting/ +/libsrc/ffdec_lib/coverage.ec +/libsrc/ffdec_lib/revision.txt +/libsrc/gif/nbproject/private/ +/libsrc/gif/build/ +/libsrc/jpproxy/nbproject/private/ +/libsrc/jpproxy/dist/ +/libsrc/jpproxy/build/ +/libsrc/LZMA/nbproject/private/ +/libsrc/ttf/nbproject/private/ +/libsrc/ttf/build/ +/libsrc/tablelayout/nbproject/private/ +/libsrc/tablelayout/build/ +/libsrc/tablelayout/dist/ +/libsrc/uploader/nbproject/private/ +/libsrc/uploader/build/ +/libsrc/uploader/dist/ +/jpexs_website.properties +/version.properties +/tools.properties +/nbproject/private/ +/libsrc/cmykjpeg/build/ +/libsrc/cmykjpeg/nbproject/private/ +/libsrc/Plugins/nbproject/private/ +/libsrc/Plugins/build/ +/libsrc/Plugins/dist/ +/libsrc/treetable/nbproject/private/ +/libsrc/treetable/build/ +/libsrc/treetable/dist/ +/libsrc/plugins/nbproject/private/ \ No newline at end of file diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java index 368232a91..9b0a162f9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java @@ -12,8 +12,10 @@ import com.jpexs.decompiler.flash.amf.amf3.types.VectorDoubleType; import com.jpexs.decompiler.flash.amf.amf3.types.DictionaryType; import com.jpexs.decompiler.flash.amf.amf3.types.VectorUIntType; import com.jpexs.decompiler.flash.amf.amf3.types.BasicType; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.NoSuchFileException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -103,7 +105,7 @@ public class AMF3InputStream extends InputStream { byte buf[] = new byte[(int) byteLength]; //how about long strings(?), will the int length be enough? int cnt = is.read(buf); if (cnt < buf.length) { - throw new IOException("EOF"); + throw new PrematureEndOfTheStreamException(); } String retString = new String(buf, "UTF-8"); return retString; @@ -131,7 +133,7 @@ public class AMF3InputStream extends InputStream { private int safeRead() throws IOException { int ret = read(); if (ret == -1) { - throw new IOException("EOF"); + throw new PrematureEndOfTheStreamException(); } return ret; } @@ -141,11 +143,11 @@ public class AMF3InputStream extends InputStream { return is.read(); } - public Object readValue() throws IOException { + public Object readValue() throws IOException, NoSerializerExistsException { return readValue(new HashMap<>()); } - public Object readValue(Map serializers) throws IOException { + public Object readValue(Map serializers) throws IOException, NoSerializerExistsException { return readValue(serializers, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); } @@ -153,7 +155,7 @@ public class AMF3InputStream extends InputStream { List objectTable, List traitsTable, List stringTable - ) throws IOException { + ) throws IOException, NoSerializerExistsException { int marker = readU8(); switch (marker) { @@ -197,7 +199,7 @@ public class AMF3InputStream extends InputStream { byte buf[] = new byte[(int) byteLength]; //TODO: long strings, int is not enough for them int cnt = is.read(buf); if (cnt < buf.length) { - throw new IOException("EOF"); + throw new PrematureEndOfTheStreamException(); } xval = new String(buf, "UTF-8"); } @@ -243,14 +245,27 @@ public class AMF3InputStream extends InputStream { if (key.isEmpty()) { break; } else { - Object val = readValue(serializers, objectTable, traitsTable, stringTable); - assocPart.add(new Pair<>(key, val)); + try { + Object val = readValue(serializers, objectTable, traitsTable, stringTable); + assocPart.add(new Pair<>(key, val)); + } catch (NoSerializerExistsException nse) { + assocPart.add(new Pair<>(key, nse.getIncompleteData())); + throw new NoSerializerExistsException(nse.getClassName(), retArray, nse); + } } } LOGGER.log(Level.FINEST, "Array value: assocSize={0}", new Object[]{assocPart.size()}); for (int i = 0; i < denseCount; i++) { - densePart.add(readValue(serializers, objectTable, traitsTable, stringTable)); + try { + densePart.add(readValue(serializers, objectTable, traitsTable, stringTable)); + } catch (NoSerializerExistsException nse) { + densePart.add(nse.getIncompleteData()); + for (int j = i + 1; j < denseCount; j++) { + densePart.add(BasicType.UNKNOWN); + } + throw new NoSerializerExistsException(nse.getClassName(), retArray, nse); + } } LOGGER.log(Level.FINER, "Array value: dense_size={0},assocSize={1}", new Object[]{densePart.size(), assocPart.size()}); return retArray; @@ -274,13 +289,17 @@ public class AMF3InputStream extends InputStream { if (objectTraitsExtFlag == 1) { String className = readUtf8Vr(stringTable); if (!serializers.containsKey(className)) { - throw new IOException("No (de)serializer for class " + className + " found"); + throw new NoSerializerExistsException(className, new ObjectType(className, null, new ArrayList<>()), null); } - retObjectType = serializers.get(className).readObject(className, is); + + MonitoredInputStream mis = new MonitoredInputStream(is); + List> serMembers = serializers.get(className).readObject(className, mis); + byte serData[] = mis.getReadData(); + retObjectType = new ObjectType(className, serData, serMembers); + LOGGER.log(Level.FINER, "Object/Traits value: customSerialized"); - traits = new Traits(className, true, new ArrayList<>());// FIXME??? - traitsTable.add(traits); - //TODO: verify this + objectTable.add(retObjectType); + return retObjectType; } else { int dynamicFlag = (int) ((objectU29 >> 3) & 1); int numSealed = (int) (objectU29 >> 4); @@ -310,8 +329,19 @@ public class AMF3InputStream extends InputStream { Object retObjectType = new ObjectType(traits.isDynamic(), sealedMembers, dynamicMembers, traits.getClassName()); objectTable.add(retObjectType); //add it before any subvalue can reference it List sealedMemberValues = new ArrayList<>(); + NoSerializerExistsException error = null; + for (int i = 0; i < traits.getSealedMemberNames().size(); i++) { - sealedMemberValues.add(readValue(serializers, objectTable, traitsTable, stringTable)); + try { + sealedMemberValues.add(readValue(serializers, objectTable, traitsTable, stringTable)); + } catch (NoSerializerExistsException nse) { + sealedMemberValues.add(nse.getIncompleteData()); + for (int j = i + 1; j < traits.getSealedMemberNames().size(); j++) { + sealedMemberValues.add(BasicType.UNKNOWN); + } + error = nse; + break; + } } for (int i = 0; i < traits.getSealedMemberNames().size(); i++) { @@ -320,8 +350,13 @@ public class AMF3InputStream extends InputStream { if (traits.isDynamic()) { String dynamicMemberName; while (!(dynamicMemberName = readUtf8Vr(stringTable)).isEmpty()) { - Object dynamicMemberValue = readValue(serializers, objectTable, traitsTable, stringTable); - dynamicMembers.add(new Pair<>(dynamicMemberName, dynamicMemberValue)); + try { + Object dynamicMemberValue = readValue(serializers, objectTable, traitsTable, stringTable); + dynamicMembers.add(new Pair<>(dynamicMemberName, dynamicMemberValue)); + } catch (NoSerializerExistsException nse) { + dynamicMembers.add(new Pair<>(dynamicMemberName, nse.getIncompleteData())); + throw new NoSerializerExistsException(nse.getClassName(), retObjectType, nse); + } } } @@ -356,7 +391,7 @@ public class AMF3InputStream extends InputStream { int byteArrayLength = (int) (byteArrayU29 >> 1); byte byteArrayBuf[] = new byte[byteArrayLength]; if (is.read(byteArrayBuf) != byteArrayLength) { - throw new IOException("EOF"); + throw new PrematureEndOfTheStreamException(); } LOGGER.log(Level.FINER, "ByteArray value: bytes[{0}]", byteArrayLength); @@ -437,12 +472,25 @@ public class AMF3InputStream extends InputStream { int fixed = readU8(); String objectTypeName = readUtf8Vr(stringTable); //uses "*" for any type List vals = new ArrayList<>(); + NoSerializerExistsException error = null; for (int i = 0; i < vectorObjectCountItems; i++) { - vals.add(readValue(serializers, objectTable, traitsTable, stringTable)); + try { + vals.add(readValue(serializers, objectTable, traitsTable, stringTable)); + } catch (NoSerializerExistsException nse) { + vals.add(nse.getIncompleteData()); + for (int j = i + 1; j < vectorObjectCountItems; j++) { + vals.add(BasicType.UNKNOWN); + } + error = nse; + break; + } } VectorObjectType retVectorObject = new VectorObjectType(fixed == 1, objectTypeName, vals); LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}, typeName:{2}]", new Object[]{fixed, vectorObjectCountItems, objectTypeName}); objectTable.add(retVectorObject); + if (error != null) { + throw new NoSerializerExistsException(error.getClassName(), retVectorObject, error); + } return retVectorObject; } else { int refIndexVectorObject = (int) (vectorObjectU29 >> 1); @@ -456,13 +504,37 @@ public class AMF3InputStream extends InputStream { int numEntries = (int) (dictionaryObjectU29 >> 1); int weakKeys = readU8(); List> data = new ArrayList<>(); + NoSerializerExistsException error = null; for (int i = 0; i < numEntries; i++) { - Object key = readValue(serializers, objectTable, traitsTable, stringTable); - Object val = readValue(serializers, objectTable, traitsTable, stringTable); + Object key; + Object val; + try { + key = readValue(serializers, objectTable, traitsTable, stringTable); + try { + val = readValue(serializers, objectTable, traitsTable, stringTable); + } catch (NoSerializerExistsException nse) { + error = nse; + val = BasicType.UNKNOWN; + } + } catch (NoSerializerExistsException nse) { + error = nse; + key = BasicType.UNKNOWN; + val = BasicType.UNKNOWN; + } + data.add(new Pair<>(key, val)); + if (error != null) { + for (int j = i + 1; j < numEntries; j++) { + data.add(new Pair<>(BasicType.UNKNOWN, BasicType.UNKNOWN)); + } + break; + } } DictionaryType retDictionary = new DictionaryType(weakKeys == 1, data); objectTable.add(retDictionary); + if (error != null) { + throw new NoSerializerExistsException(error.getClassName(), retDictionary, error); + } return retDictionary; } else { int refIndexDictionary = (int) (dictionaryObjectU29 >> 1); @@ -470,8 +542,31 @@ public class AMF3InputStream extends InputStream { return objectTable.get(refIndexDictionary); } default: - return null; + throw new UnsupportedValueType(marker); } } + private class MonitoredInputStream extends InputStream { + + private final InputStream is; + private ByteArrayOutputStream baos; + + public MonitoredInputStream(InputStream is) { + this.is = is; + this.baos = new ByteArrayOutputStream(); + } + + @Override + public int read() throws IOException { + int ret = is.read(); + if (ret > -1) { + baos.write(ret); + } + return ret; + } + + public byte[] getReadData() { + return baos.toByteArray(); + } + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java index 85125208d..eb9daa7ba 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java @@ -46,6 +46,9 @@ public class AMF3Tools { if (((List) Arrays.asList(String.class, Long.class, Double.class, BasicType.class, Boolean.class)).contains(object.getClass())) { return; } + if (object instanceof BasicType) { + return; + } int prevRef = 0; if (referenceCount.containsKey(object)) { prevRef = referenceCount.get(object); @@ -85,7 +88,11 @@ public class AMF3Tools { if (object instanceof String) { return "\"" + Helper.escapeActionScriptString((String) object) + "\""; } - if (((List) Arrays.asList(Long.class, Double.class, BasicType.class, Boolean.class)).contains(object.getClass())) { + if (((List) Arrays.asList(Long.class, Double.class, Boolean.class)).contains(object.getClass())) { + return object.toString(); + } + + if (object instanceof BasicType) { return object.toString(); } @@ -122,35 +129,58 @@ public class AMF3Tools { ret.append(indent(level + 1)).append("\"type\": \"Object\",\r\n"); ret.append(addId); ret.append(indent(level + 1)).append("\"className\": ").append(amfToString(processedObjects, level, ot.getClassName(), referenceCount, objectAlias)).append(",\r\n"); - ret.append(indent(level + 1)).append("\"dynamic\": ").append(ot.isDynamic()).append(",\r\n"); - if (!ot.getSealedMembers().isEmpty()) { - ret.append(indent(level + 1)).append("\"sealedMembers\": {\r\n"); - for (int i = 0; i < ot.getSealedMembers().size(); i++) { - Pair member = ot.getSealedMembers().get(i); - ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 1, member.getSecond(), referenceCount, objectAlias)); - if (i < ot.getSealedMembers().size() - 1) { - ret.append(",\r\n"); - } else { + if (ot.isSerialized()) { + byte[] serData = ot.getSerializedData(); + if (serData == null) { + ret.append(indent(level + 1)).append("\"serialized\": unknown\r\n"); + } else { + ret.append(indent(level + 1)).append("\"serialized\": 0x").append(javax.xml.bind.DatatypeConverter.printHexBinary(serData)).append(",\r\n"); + if (!ot.getSerializedMembers().isEmpty()) { + ret.append(indent(level + 1)).append("\"unserializedMembers\": {\r\n"); + for (int i = 0; i < ot.getSerializedMembers().size(); i++) { + Pair member = ot.getSerializedMembers().get(i); + ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 1, member.getSecond(), referenceCount, objectAlias)); + if (i < ot.getSerializedMembers().size() - 1) { + ret.append(",\r\n"); + } else { + ret.append("\r\n"); + } + } + ret.append(indent(level + 1)).append("}"); ret.append("\r\n"); } } - ret.append(indent(level + 1)).append("}"); - if (!ot.getDynamicMembers().isEmpty()) { - ret.append(","); - } - ret.append("\r\n"); - } - if (!ot.getDynamicMembers().isEmpty()) { - ret.append(indent(level + 1)).append("\"dynamicMembers\": {\r\n"); - for (int i = 0; i < ot.getDynamicMembers().size(); i++) { - Pair member = ot.getDynamicMembers().get(i); - ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 2, member.getSecond(), referenceCount, objectAlias)); - if (i < ot.getDynamicMembers().size() - 1) { + } else { + ret.append(indent(level + 1)).append("\"dynamic\": ").append(ot.isDynamic()).append(",\r\n"); + if (!ot.getSealedMembers().isEmpty()) { + ret.append(indent(level + 1)).append("\"sealedMembers\": {\r\n"); + for (int i = 0; i < ot.getSealedMembers().size(); i++) { + Pair member = ot.getSealedMembers().get(i); + ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 1, member.getSecond(), referenceCount, objectAlias)); + if (i < ot.getSealedMembers().size() - 1) { + ret.append(",\r\n"); + } else { + ret.append("\r\n"); + } + } + ret.append(indent(level + 1)).append("}"); + if (!ot.getDynamicMembers().isEmpty()) { ret.append(","); } ret.append("\r\n"); } - ret.append(indent(level + 1)).append("}\r\n"); + if (!ot.getDynamicMembers().isEmpty()) { + ret.append(indent(level + 1)).append("\"dynamicMembers\": {\r\n"); + for (int i = 0; i < ot.getDynamicMembers().size(); i++) { + Pair member = ot.getDynamicMembers().get(i); + ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 2, member.getSecond(), referenceCount, objectAlias)); + if (i < ot.getDynamicMembers().size() - 1) { + ret.append(","); + } + ret.append("\r\n"); + } + ret.append(indent(level + 1)).append("}\r\n"); + } } ret.append(indent(level)).append("}"); } else if (object instanceof ArrayType) { @@ -198,7 +228,7 @@ public class AMF3Tools { ret.append("\r\n"); } ret.append(indent(level + 1)).append("}\r\n"); - ret.append(indent(level)).append("}"); + ret.append(indent(level) + "}"); } else if (object instanceof ByteArrayType) { ByteArrayType ba = (ByteArrayType) object; byte data[] = ba.getData(); @@ -228,10 +258,6 @@ public class AMF3Tools { + addId + indent(level + 1) + "\"value\": " + amfToString(processedObjects, level, ((XmlType) object).getData(), referenceCount, objectAlias) + "\r\n" + indent(level) + "}"; - } else if (object == BasicType.NULL) { - return "null"; - } else if (object == BasicType.UNDEFINED) { - return "undefined"; } else { throw new IllegalArgumentException("Unsupported type: " + object.getClass()); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/NoSerializerExistsException.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/NoSerializerExistsException.java new file mode 100644 index 000000000..f4cd492f8 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/NoSerializerExistsException.java @@ -0,0 +1,25 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +public class NoSerializerExistsException extends Exception { + + private final String className; + private final Object incompleteData; + + /*public NoSerializerExistsException(String className, Object incompleteData) { + this(className, incompleteData, null); + }*/ + public NoSerializerExistsException(String className, Object incompleteData, Throwable cause) { + super("Cannot read AMF - no deserializer defined for class \"" + className + "\".", cause); + this.className = className; + this.incompleteData = incompleteData; + } + + public String getClassName() { + return className; + } + + public Object getIncompleteData() { + return incompleteData; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ObjectTypeSerializeHandler.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ObjectTypeSerializeHandler.java index 67fe64ac2..1acbc0b71 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ObjectTypeSerializeHandler.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/ObjectTypeSerializeHandler.java @@ -1,12 +1,13 @@ package com.jpexs.decompiler.flash.amf.amf3; -import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.List; public interface ObjectTypeSerializeHandler { - public ObjectType readObject(String className, InputStream is); + public List> readObject(String className, InputStream is) throws IOException; - public void writeObject(ObjectType val, OutputStream os); + public void writeObject(List> members, OutputStream os) throws IOException; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException.java new file mode 100644 index 000000000..8c85fa2df --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException.java @@ -0,0 +1,11 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +import java.io.IOException; + +public class PrematureEndOfTheStreamException extends IOException { + + public PrematureEndOfTheStreamException() { + super("Premature end of the stream reached"); + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/UnsupportedValueType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/UnsupportedValueType.java new file mode 100644 index 000000000..1228a4cd1 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/UnsupportedValueType.java @@ -0,0 +1,16 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +public class UnsupportedValueType extends RuntimeException { + + private int marker; + + public UnsupportedValueType(int marker) { + super("Unsupported type of value - marker: 0x" + Integer.toHexString(marker)); + this.marker = marker; + } + + public int getMarker() { + return marker; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java new file mode 100644 index 000000000..ebdb1b2ad --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java @@ -0,0 +1,40 @@ +package com.jpexs.decompiler.flash.amf.amf3.types; + +import com.jpexs.decompiler.flash.amf.amf3.AMF3Tools; +import java.util.ArrayList; +import java.util.List; +import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; + +public abstract class AbstractVectorType implements WithSubValues { + + private boolean fixed; + private List values; + + public boolean isFixed() { + return fixed; + } + + public AbstractVectorType(boolean fixed, List values) { + this.values = values; + this.fixed = fixed; + } + + public List getValues() { + return values; + } + + @Override + public List getSubValues() { + List ret = new ArrayList<>(); + ret.addAll(values); + return ret; + } + + public abstract String getTypeName(); + + @Override + public String toString() { + return AMF3Tools.amfToString(this); + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java new file mode 100644 index 000000000..a3caf7030 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java @@ -0,0 +1,46 @@ +package com.jpexs.decompiler.flash.amf.amf3.types; + +import com.jpexs.decompiler.flash.amf.amf3.AMF3Tools; +import com.jpexs.decompiler.flash.amf.amf3.Pair; +import com.jpexs.helpers.Helper; +import java.util.ArrayList; +import java.util.List; +import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; + +public class ArrayType implements WithSubValues { + + private List denseValues; + private List> associativeValues; + + public ArrayType(List denseValues, List> associativeValues) { + this.denseValues = denseValues; + this.associativeValues = associativeValues; + } + + public List getDenseValues() { + return denseValues; + } + + public List> getAssociativeValues() { + return associativeValues; + } + + @Override + public String toString() { + return AMF3Tools.amfToString(this); + } + + @Override + public List getSubValues() { + List ret = new ArrayList<>(); + for (Pair p : associativeValues) { + ret.add(p.getFirst()); + ret.add(p.getSecond()); + } + for (Object v : denseValues) { + ret.add(v); + } + return ret; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/BasicType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/BasicType.java new file mode 100644 index 000000000..1d5752451 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/BasicType.java @@ -0,0 +1,26 @@ +package com.jpexs.decompiler.flash.amf.amf3.types; + +public enum BasicType { + NULL { + @Override + public String toString() { + return "null"; + } + + }, + UNDEFINED { + @Override + public String toString() { + return "undefined"; + } + + }, + //Special types for errors while reading + UNKNOWN { + @Override + public String toString() { + return "unknown"; + } + + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java new file mode 100644 index 000000000..93cedf228 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java @@ -0,0 +1,22 @@ +package com.jpexs.decompiler.flash.amf.amf3.types; + +import com.jpexs.decompiler.flash.amf.amf3.AMF3Tools; + +public class ByteArrayType { + + private byte[] data; + + public ByteArrayType(byte[] data) { + this.data = data; + } + + public byte[] getData() { + return data; + } + + @Override + public String toString() { + return AMF3Tools.amfToString(this); + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java new file mode 100644 index 000000000..821a8a0cb --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java @@ -0,0 +1,30 @@ +package com.jpexs.decompiler.flash.amf.amf3.types; + +import com.jpexs.decompiler.flash.amf.amf3.AMF3Tools; +import java.util.Date; + +public class DateType { + + private double val; + + public DateType(double val) { + this.val = val; + } + + public double getVal() { + return val; + } + + public void setVal(double val) { + this.val = val; + } + + @Override + public String toString() { + return AMF3Tools.amfToString(this); + } + + public Date toDate() { + return new Date((long) val); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java new file mode 100644 index 000000000..29cf399f2 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java @@ -0,0 +1,38 @@ +package com.jpexs.decompiler.flash.amf.amf3.types; + +import com.jpexs.decompiler.flash.amf.amf3.AMF3Tools; +import com.jpexs.decompiler.flash.amf.amf3.Pair; +import java.util.ArrayList; +import java.util.List; +import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; + +public class DictionaryType implements WithSubValues { + + private boolean weakKeys; + private List> pairs; + + public DictionaryType(boolean weakKeys, List> pairs) { + this.weakKeys = weakKeys; + this.pairs = pairs; + } + + public List> getPairs() { + return pairs; + } + + @Override + public List getSubValues() { + List ret = new ArrayList<>(); + for (Pair p : pairs) { + ret.add(p.getFirst()); + ret.add(p.getSecond()); + } + return ret; + } + + @Override + public String toString() { + return AMF3Tools.amfToString(this); + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java new file mode 100644 index 000000000..3e0dfb4cc --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java @@ -0,0 +1,116 @@ +package com.jpexs.decompiler.flash.amf.amf3.types; + +import com.jpexs.decompiler.flash.amf.amf3.AMF3Tools; +import com.jpexs.decompiler.flash.amf.amf3.Pair; +import com.jpexs.helpers.Helper; +import java.util.ArrayList; +import java.util.List; +import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; + +public class ObjectType implements WithSubValues { + + private boolean dynamic; + private List> sealedMembers; + private List> dynamicMembers; + private List> serializedMembers; + private String className; + //null = not serialized or unknown + private byte[] serializedData = null; + private boolean serialized; + + public boolean isSerialized() { + return serialized; + } + + public ObjectType(String className, byte[] serializedData, List> serializedMembers) { + this.className = className; + this.serializedData = serializedData; + this.serializedMembers = serializedMembers; + this.dynamicMembers = new ArrayList<>(); + this.sealedMembers = new ArrayList<>(); + this.serialized = true; + } + + public ObjectType(boolean dynamic, List> sealedMembers, List> dynamicMembers, String className) { + this.dynamic = dynamic; + this.sealedMembers = sealedMembers; + this.dynamicMembers = dynamicMembers; + this.className = className; + this.serializedMembers = new ArrayList<>(); + this.serialized = false; + } + + public boolean isDynamic() { + return dynamic; + } + + public List> getDynamicMembers() { + return dynamicMembers; + } + + public List> getSealedMembers() { + return sealedMembers; + } + + public String getClassName() { + return className; + } + + public void setDynamic(boolean dynamic) { + this.dynamic = dynamic; + } + + public void setDynamicMembers(List> dynamicMembers) { + this.dynamicMembers = dynamicMembers; + } + + public void setSealedMembers(List> sealedMembers) { + this.sealedMembers = sealedMembers; + } + + public void setClassName(String className) { + this.className = className; + } + + @Override + public List getSubValues() { + List ret = new ArrayList<>(); + for (Pair p : dynamicMembers) { + ret.add(p.getFirst()); + ret.add(p.getSecond()); + } + for (Pair p : sealedMembers) { + ret.add(p.getFirst()); + ret.add(p.getSecond()); + } + + for (Pair p : serializedMembers) { + ret.add(p.getFirst()); + ret.add(p.getSecond()); + } + + return ret; + } + + @Override + public String toString() { + return AMF3Tools.amfToString(this); + } + + public void setSerializedData(byte[] serializedData) { + this.serializedData = serializedData; + } + + public byte[] getSerializedData() { + return serializedData; + } + + public void setSerializedMembers(List> serializedMembers) { + this.serializedMembers = serializedMembers; + } + + public List> getSerializedMembers() { + return serializedMembers; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorDoubleType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorDoubleType.java new file mode 100644 index 000000000..6755a08db --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorDoubleType.java @@ -0,0 +1,16 @@ +package com.jpexs.decompiler.flash.amf.amf3.types; + +import java.util.List; + +public class VectorDoubleType extends AbstractVectorType { + + public VectorDoubleType(boolean fixed, List values) { + super(fixed, values); + } + + @Override + public String getTypeName() { + return "Number"; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorIntType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorIntType.java new file mode 100644 index 000000000..2841545a8 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorIntType.java @@ -0,0 +1,16 @@ +package com.jpexs.decompiler.flash.amf.amf3.types; + +import java.util.List; + +public class VectorIntType extends AbstractVectorType { + + public VectorIntType(boolean fixed, List values) { + super(fixed, values); + } + + @Override + public String getTypeName() { + return "int"; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorObjectType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorObjectType.java new file mode 100644 index 000000000..98bad08cb --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorObjectType.java @@ -0,0 +1,21 @@ +package com.jpexs.decompiler.flash.amf.amf3.types; + +import com.jpexs.helpers.Helper; +import java.util.ArrayList; +import java.util.List; + +public class VectorObjectType extends AbstractVectorType { + + private String typeName; + + public VectorObjectType(boolean fixed, String typeName, List values) { + super(fixed, values); + this.typeName = typeName; + } + + @Override + public String getTypeName() { + return typeName; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorUIntType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorUIntType.java new file mode 100644 index 000000000..a130fd3ac --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/VectorUIntType.java @@ -0,0 +1,17 @@ +package com.jpexs.decompiler.flash.amf.amf3.types; + +import java.util.ArrayList; +import java.util.List; + +public class VectorUIntType extends AbstractVectorType { + + public VectorUIntType(boolean fixed, List values) { + super(fixed, values); + } + + @Override + public String getTypeName() { + return "uint"; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlDocType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlDocType.java new file mode 100644 index 000000000..37207bccf --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlDocType.java @@ -0,0 +1,24 @@ +package com.jpexs.decompiler.flash.amf.amf3.types; + +public class XmlDocType { + + private String data; + + public XmlDocType(String data) { + this.data = data; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + @Override + public String toString() { + return data; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlType.java new file mode 100644 index 000000000..bd0e77027 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlType.java @@ -0,0 +1,24 @@ +package com.jpexs.decompiler.flash.amf.amf3.types; + +public class XmlType { + + private String data; + + public XmlType(String data) { + this.data = data; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + @Override + public String toString() { + return data; + } + +} diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java new file mode 100644 index 000000000..7a83cb0a5 --- /dev/null +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java @@ -0,0 +1,171 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +import com.jpexs.decompiler.flash.amf.amf3.types.ArrayType; +import com.jpexs.decompiler.flash.amf.amf3.types.DictionaryType; +import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorObjectType; +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import static org.testng.Assert.*; +import org.testng.annotations.AfterTest; +import org.testng.annotations.Test; + +public class Amf3InputStreamTest { + + private FileInputStream fis; + private AMF3InputStream is; + + @AfterTest + public void deinitStream() { + if (is != null) { + try { + is.close(); + } catch (IOException ex) { + //ignore + } + is = null; + } + if (fis != null) { + try { + fis.close(); + } catch (IOException ex) { + //ignore + } + fis = null; + } + } + + private void initStream(String fileName) throws FileNotFoundException { + fis = new FileInputStream("testdata/amf3/generated/" + fileName); + is = new AMF3InputStream(fis); + } + + @Test + public void testReadObject() throws IOException, NoSerializerExistsException { + initStream("all.bin"); + is.readValue(); + } + + private Map getSerializers() { + Map serializers = new HashMap<>(); + serializers.put("CustomClass", new ObjectTypeSerializeHandler() { + @Override + public List> readObject(String className, InputStream is) throws IOException { + List> members = new ArrayList<>(); + members.add(new Pair<>("val8", (long) is.read())); + members.add(new Pair<>("val32", (long) ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read())))); + return members; + } + + @Override + public void writeObject(List> members, OutputStream os) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + }); + return serializers; + } + + @Test(expectedExceptions = NoSerializerExistsException.class) + public void testReadCustomSerializedNeedsSerializer() throws IOException, NoSerializerExistsException { + initStream("custom.bin"); + is.readValue(); //needs deserializer for CustomClass + } + + @Test + public void testReadCustomSerialized() throws IOException, NoSerializerExistsException { + initStream("custom.bin"); + is.readValue(getSerializers()); + } + + @Test + public void testNoSerializerHandlingInObjectDynamicProp() throws IOException { + initStream("noserializer_object_dynamic.bin"); + try { + is.readValue(); + } catch (NoSerializerExistsException ex) { + assertTrue(ex.getIncompleteData() instanceof ObjectType, "Expected datatype: ObjectType, Actual datatype: " + ex.getIncompleteData().getClass()); + //TODO: examinate the data more + } + } + + @Test + public void testNoSerializerHandlingInObjectSealedProp() throws IOException { + initStream("noserializer_object_sealed.bin"); + try { + is.readValue(); + } catch (NoSerializerExistsException ex) { + assertTrue(ex.getIncompleteData() instanceof ObjectType, "Expected datatype: ObjectType, Actual datatype: " + ex.getIncompleteData().getClass()); + //TODO: examinate the data more + } + } + + @Test + public void testNoSerializerHandlingInArrayDense() throws IOException { + initStream("noserializer_array_dense.bin"); + try { + is.readValue(); + } catch (NoSerializerExistsException ex) { + assertTrue(ex.getIncompleteData() instanceof ArrayType, "Expected datatype: ArrayType, Actual datatype: " + ex.getIncompleteData().getClass()); + //TODO: examinate the data more + } + } + + @Test + public void testNoSerializerHandlingInArrayAssociative() throws IOException { + initStream("noserializer_array_associative.bin"); + try { + is.readValue(); + } catch (NoSerializerExistsException ex) { + assertTrue(ex.getIncompleteData() instanceof ArrayType, "Expected datatype: ArrayType, Actual datatype: " + ex.getIncompleteData().getClass()); + //TODO: examinate the data more + } + } + + @Test + public void testNoSerializerHandlingInVector() throws IOException { + initStream("noserializer_vector.bin"); + try { + is.readValue(); + } catch (NoSerializerExistsException ex) { + assertTrue(ex.getIncompleteData() instanceof VectorObjectType, "Expected datatype: VectorObjectType, Actual datatype: " + ex.getIncompleteData().getClass()); + //TODO: examinate the data more + } + } + + @Test + public void testNoSerializerHandlingInDictionaryValues() throws IOException { + initStream("noserializer_dictionary_value.bin"); + try { + is.readValue(); + } catch (NoSerializerExistsException ex) { + assertTrue(ex.getIncompleteData() instanceof DictionaryType, "Expected datatype: DictionaryType, Actual datatype: " + ex.getIncompleteData().getClass()); + //TODO: examinate the data more + } + } + + @Test + public void testNoSerializerHandlingInDictionaryKeys() throws IOException { + initStream("noserializer_dictionary_key.bin"); + try { + is.readValue(); + } catch (NoSerializerExistsException ex) { + assertTrue(ex.getIncompleteData() instanceof DictionaryType, "Expected datatype: DictionaryType, Actual datatype: " + ex.getIncompleteData().getClass()); + //TODO: examinate the data more + } + } + + @Test(expectedExceptions = UnsupportedValueType.class) + public void testUnsupportedMarker() throws IOException, NoSerializerExistsException { + final int UNSUPPORTED_MARKER = 100; + is = new AMF3InputStream(new ByteArrayInputStream(new byte[]{UNSUPPORTED_MARKER})); + is.readValue(); + } +} diff --git a/libsrc/ffdec_lib/testdata/amf3/AmfTest.as b/libsrc/ffdec_lib/testdata/amf3/AmfTest.as new file mode 100644 index 000000000..37c7934fc --- /dev/null +++ b/libsrc/ffdec_lib/testdata/amf3/AmfTest.as @@ -0,0 +1,169 @@ +package +{ + import flash.display.Sprite; + import flash.utils.ByteArray; + import flash.filesystem.File; + import flash.filesystem.FileStream; + import flash.filesystem.FileMode; + + import flash.utils.Dictionary; + import flash.geom.Point; + import flash.xml.XMLDocument; + + import flash.net.registerClassAlias; + + /** + * Generating test data file + */ + public class AmfTest extends Sprite + { + + private function writeToFile(filename:String,data) + { + var urlStr:String = "file:///"+File.applicationDirectory.nativePath+"/generated/"+filename; + var file:File = new File() ; + file.url = urlStr; + var fileStream:FileStream = new FileStream(); + fileStream.open(file, FileMode.WRITE); + var bytes:ByteArray = new ByteArray(); + bytes.writeObject(data); + fileStream.writeBytes(bytes, 0, bytes.length); + fileStream.close(); + + } + + public function AmfTest() { + + + + var ar = ["a","b","c"]; + + var asoc_array = [1,2,3]; + asoc_array["key1"] = 5; + asoc_array["key2"] = 6; + + var ba:ByteArray = new ByteArray(); + ba.writeByte(65); + ba.writeByte(66); + ba.writeByte(67); + + + var vector_int:Vector. = new [-10,20,-30,40]; + var vector_uint:Vector. = new [10,20,30,40]; + var vector_double:Vector. = new [-10.1,20.2,-30.3,40.4]; + var vector_string:Vector. = new ["x","y","z"]; + var vector_point:Vector. = new [new Point(10,20),new Point(30,40),new Point(50,60)]; + + var dict = new Dictionary(); + dict["dkey1"] = "TestOne"; + dict["dkey2"] = "TestTwo"; + + var txmldoc:XMLDocument = new XMLDocument("aa"); + + var txml:XML = + Hello + Hi + ; + + var date = new Date(); + + var me = { + "01_int":5, + "02_negative-int":-5, + "03_string":"String", + "04_true":true, + "05_false":false, + "06_undefined":undefined, + "07_null":null, + "08_double":5.6, + "09_negative-double":-5.6, + "10_array":ar, + "11_asoc_array": asoc_array, + "12_date":date, + "13_xml":txml, + "14_byteArray":ba, + "15_vectorInt":vector_int, + "16_vectorUInt":vector_uint, + "17_vectorDouble":vector_double, + "18_vectorString":vector_string, + "19_vectorPoint":vector_point, + "20_dictionary":dict, + "21_xmldoc":txmldoc, + "22_ref_string":"String", + "23_ref_array":ar, + "24_ref_date":date, + "25_ref_byteArray":ba, + "26_ref_vectorInt":vector_int, + "27_ref_vectorUInt":vector_uint, + "28_ref_vectorDouble":vector_double, + "29_ref_vectorString":vector_string, + "30_ref_dictionary":dict, + "31_ref_xml":txml, + "32_ref_xmldoc":txmldoc + }; + me["33_me"] = me; + + writeToFile("all.bin",me); + + var custom = new CustomClass(); + custom.setVal8(127); + custom.setVal32(-2500); + registerClassAlias("CustomClass", CustomClass); + + var cust_obj = { + "member1":5, + "member2":custom, + "member3":27 + }; + + writeToFile("custom.bin",cust_obj); + + writeToFile("noserializer_object_dynamic.bin",{ + "a":5, + "b":8, + "c":custom, + "d":6, + "e":7, + "f":26 + }); + writeToFile("noserializer_array_dense.bin", + ["a","b",custom,"d","e","f"] + ); + + var arx = []; + arx["a"] = 1; + arx["b"] = 2; + arx["c"] = custom; + arx["d"] = 4; + arx["e"] = 5; + arx["f"] = 6; + writeToFile("noserializer_array_associative.bin", arx); + + + writeToFile("noserializer_vector.bin", new ["a","b",custom,"d","e","f"]); + + var nsdict = new Dictionary(); + nsdict["a"] = "One"; + nsdict["b"] = "Two"; + nsdict["c"] = custom; + nsdict["d"] = "Three"; + nsdict["e"] = "Four"; + nsdict["f"] = "Five"; + + writeToFile("noserializer_dictionary_value.bin", nsdict); + + var nsdict2 = new Dictionary(); + nsdict2["a"] = "One"; + nsdict2["b"] = "Two"; + nsdict2[custom] = "Three"; + nsdict2["d"] = "Three"; + nsdict2["e"] = "Four"; + nsdict2["f"] = "Five"; + + writeToFile("noserializer_dictionary_key.bin", nsdict2); + + var objWithCustom:ObjectWithCustom = new ObjectWithCustom("1","2",custom,"4","5","6"); + writeToFile("noserializer_object_sealed.bin", objWithCustom); + } + } +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/amf3/AmfTest.swf b/libsrc/ffdec_lib/testdata/amf3/AmfTest.swf new file mode 100644 index 0000000000000000000000000000000000000000..eed907c1861886a822e576ff7dfffc76e21f47fb GIT binary patch literal 2287 zcmV!QZnT84W^-kDu!b~qNjlRVSVp3_xhTH3JNC+2)1J*>=-p>|kv2*_BK)@TdzxW~s5 zRX~U5S-?);A+G44YL3M0*f=<)^i{biPBGOIR$lYO&{zPAusrYQ+MNi=aD#T4!$ zPkS*y6Mr~&jb2>K)qny&rD&(ESY3$E?lK{?1zUms&{I3Vb0qs^Bhy=k46Lmquz0^2=DPOlJJ<^86bmevwHJ?vowHMJJkVNIyH^$+ss7D}1yC^( zU|@0XqC>9YPhfgpHXPKwCGx^8;YezKG3wL;ajXJz=6@k}F;@b}N9s>lrdWM=RDK8J z)H;3wIZCUjk+K<#DAIC4ZphLg;tgnH#Nx)hh>LY3&g1DIz;S|@foq&RpR!&RHJl0z zTASxV+dJRXf%F7H$|VN_Y_bCA>OxV?>W8Wtks`k_^Fhm&u?shUD`}8xJ=a1LXzA}k zk~s)Pu#=AikvA!{cd5{vx|l;cb_P{r)@<7x3(wcuK-|vRZ*;mT9@Jh+M_iauoSSe* z@p411!q`zbj(Cs%3|X(yMzNYTl7Xhd&gzpLxo&yjt3}q4jXJcq}Pb zwt%?oRm|UIoctD5U3+Rm=G8;x@AOx`T~_3h+`2w5a#1n}@70MJ3Xo3p+T3I++@V7z zmSl<^GJFrjaZYTAkly%`szHoYQ8n8he+@obP0yVXQf6~~C7&QCY9t^L;!s~55fz{t z1?jLuKTk%LKW+uFp$}fLOQqJ&g7P>{N$d&QF4I8`R9u!ijY>MDa9vPJG|){vZ8+N2^sn}%hugUe0;^{2od(jT9fttWLq-8?jFUt zRYk7Hzq7yMI&+GzI4ggp`FsKkd9+Y>V*4)G&g&vSK)CN?75E7hyhh;YY3pLDvME=x0rzkptYI&wX z&p|T?(KX=_N4DCT>4-PLep^2_f<{*~1(dD7Sw?OE>1|~uISGmBW#&jzop%KhvPaRz z4BgYSWq>SVBWaV&gN9Vb7(rv3UWySHa+*{QEb5ZS@F}nMm9RTn@eP)A-+dha-M~f} zir$qO(X@NKI%P4_g^GO|-+W3oFa;BS{j=5n4yJ*5OWSH$0Mk7CRzTq-SvU6PI z>s2BIa%{DZHd{5&b0_J?{muqEY zR|sQdHUfwbk}rN~%FJyMSg2ohN;}4WeXP8e+iAcI*rrcKB9g2E^Ih}2E^sEh#_m-H zC3J8eTwrv0kXAax=Ds5Ac*~H?_;)#biJL-r-~c3V6{u{J>c=mbh>OV;VM&K0dFSJFjvq} zZY4zyhzL(Gbt~y2lZtJcWt{-z*msF0e14)&T=OjN^jM->mVWLc*0cy}tDAw~8=qNMdbzho@V*$hsz;~=jO*(@d5O8+#559UcNEVhaDPAHRz?CD#-Ju z#+Kz67ZVOk08MPH@D|m)j4ajoL>KEceTL;>8>5qoRpcwQ6hhxHJr?t-qmvU+PnI8S z>4r>Tv*Y3T=VR1C;Ao-3q&Grl-IH6YFVweuk1v#Zyu8{X=CS%&Jltcg@)JSxxuVG4P*uT+js2uH3Fl&D1>wBuGkCI_edjN=Wt+Vc=`3Gq(m3)O~< zL^W-I4Ky2TO2i5?NmgXwCo2Gz4OYcK2T6DkG6&ZH-PNewkf77bQFm&*Y%vP7gWf^y zIiMe@M*s*!>K(0idcx + + samples.flex.HelloWorld + 1 + HelloWorld + + AmfTest.swf + true + none + true + 400 + 200 + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/all.bin b/libsrc/ffdec_lib/testdata/amf3/generated/all.bin new file mode 100644 index 0000000000000000000000000000000000000000..22b5dc4c008098fca024f38cc36163af09c22079 GIT binary patch literal 765 zcmYjP-EPw`6m}ZNrOCQdb^Me-+o_=S2F-T*D@9c|b^#I+NQ`UCZQLm$OQbX%D_1-L zx9~W90C))=f*UT1<5+Y@a*mGueU8s}KHD*>n6NO;pmj`#EEP+3BNkbbzKOGH1?+aa zKLCIunSaaNJEjALnQt<2mZp4jP(C|?+WX_Sfs1vM-b`AGgmac{AQmVkK zEDhr;RQ;gx3p`=5xZ+uOBVGo{dL9XD`*5kEK?%FPj)G)SCjK%>rv99#Q_lUOZq;M zI^Qn?!|od9#=M0`(6%kugDv^>A#LIzTgQP|hOr1hn~XKDXeuY7F&ZNCi|Z9#>pl+J zWTc@Ns@YlvY@d57X8j^>r*D7C_%*vI>IWWC!pSQfrgX479VNwU5k*O%#{aEQ*M&ST z8S3AClrA}}NMisycZpmrxq5XxU`rmYM5*iHV*Y#S4x414>AA;BZA|yWk^({LwZBc$ uXM2yq6FO1X6xP&wJZ@l*>A`nS==cso%kZR1ynNWs(=Hv|IabeZ8UFx?Amuaw literal 0 HcmV?d00001 diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/custom.bin b/libsrc/ffdec_lib/testdata/amf3/generated/custom.bin new file mode 100644 index 000000000..c3bed6b07 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/amf3/generated/custom.bin @@ -0,0 +1,3 @@ + + member3member1member2 +CustomClassÿÿö< \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_array_associative.bin b/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_array_associative.bin new file mode 100644 index 000000000..c1b45c8b6 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_array_associative.bin @@ -0,0 +1,2 @@ + fabc +CustomClassÿÿö<de \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_array_dense.bin b/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_array_dense.bin new file mode 100644 index 000000000..5438f07e1 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_array_dense.bin @@ -0,0 +1,2 @@ + ab +CustomClassÿÿö<def \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_dictionary_key.bin b/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_dictionary_key.bin new file mode 100644 index 0000000000000000000000000000000000000000..24d9b65ac5dc1a4d7c6463662f0261260e75e4aa GIT binary patch literal 68 zcmWgSWng1YW8-woEK6l$PGn>E&jZp)Z0sTB`CRPc&ZWgA`MJ(HiN(eB|NnopVdDt<8 literal 0 HcmV?d00001 diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_dictionary_value.bin b/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_dictionary_value.bin new file mode 100644 index 0000000000000000000000000000000000000000..05d9b4e62b0003c531964e7750087e7fb8c8f326 GIT binary patch literal 69 zcmWgSWng1YW8-woEK6l$PGn>E&jZp)Z0sTB`E1O|T#l`ji|9`V# VV@_e?4#_A=1*%I0YRoS!0su6(614yT literal 0 HcmV?d00001 diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_object_dynamic.bin b/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_object_dynamic.bin new file mode 100644 index 000000000..e7793c3ae --- /dev/null +++ b/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_object_dynamic.bin @@ -0,0 +1,3 @@ + + fabc +CustomClassÿÿö<de \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_object_sealed.bin b/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_object_sealed.bin new file mode 100644 index 000000000..cfaf1f8c4 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_object_sealed.bin @@ -0,0 +1,3 @@ + +ccfadbe +CustomClassÿÿö<61425 \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_vector.bin b/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_vector.bin new file mode 100644 index 0000000000000000000000000000000000000000..ff6653f8d4fd994cc1744c594fccd3dc232b8b08 GIT binary patch literal 38 tcmWgOWng4uPGnU9b1EBi8UU=K3Wfjx literal 0 HcmV?d00001 diff --git a/libsrc/ffdec_lib/testdata/amf3/run.bat b/libsrc/ffdec_lib/testdata/amf3/run.bat new file mode 100644 index 000000000..e7aa460f3 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/amf3/run.bat @@ -0,0 +1,20 @@ +@echo off +set ISDEBUG=false +if "%1" == "debug" goto blockset +goto block2 +:blockset +set ISDEBUG=true +:block2 +set AIRPATH=c:\air +set COMPILERPATH=%AIRPATH%\bin\amxmlc.bat +if not exist %COMPILERPATH% goto notex +call %COMPILERPATH% -warnings=false -debug=%ISDEBUG% AmfTest.as>NUL +if errorlevel==1 goto failed +goto end +:notex +echo AIR SDK not found. Download and unpack Flex SDK into C:\air directory, then run build again +goto end +:failed +pause +:end +%AIRPATH%\bin\adl.exe app.xml \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/as3/CustomClass.as b/libsrc/ffdec_lib/testdata/as3/CustomClass.as new file mode 100644 index 000000000..3f268b9dc --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3/CustomClass.as @@ -0,0 +1,34 @@ +package +{ + import flash.utils.IExternalizable; + import flash.utils.IDataOutput; + import flash.utils.IDataInput; + + public class CustomClass implements IExternalizable + { + + private var val8:int; + private var val32:int; + public function CustomClass() + { + } + + public function setVal8(v:int){ + this.val8 = v; + } + public function setVal32(v:int){ + this.val32 = v; + } + + public function writeExternal(output:IDataOutput):void { + output.writeByte(val8); + output.writeInt(val32); + + } + + public function readExternal(input:IDataInput):void { + this.val8 = input.readByte(); + this.val32 = input.readInt(); + } + } +} \ No newline at end of file diff --git a/libsrc/ttf/nbproject/build-impl.xml b/libsrc/ttf/nbproject/build-impl.xml index 41c1b513d..0e2c1d29f 100644 --- a/libsrc/ttf/nbproject/build-impl.xml +++ b/libsrc/ttf/nbproject/build-impl.xml @@ -1,1413 +1,1419 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set src.dir - Must set test.src.dir - Must set build.dir - Must set dist.dir - Must set build.classes.dir - Must set dist.javadoc.dir - Must set build.test.classes.dir - Must set build.test.results.dir - Must set build.classes.excludes - Must set dist.jar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - No tests executed. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set JVM to use for profiling in profiler.info.jvm - Must set profiler agent JVM arguments in profiler.info.jvmargs.agent - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - To run this application from the command line without Ant, try: - - java -jar "${dist.jar.resolved}" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set run.class - - - - Must select one file in the IDE or set run.class - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set debug.class - - - - - Must select one file in the IDE or set debug.class - - - - - Must set fix.includes - - - - - - - - - - This target only works when run from inside the NetBeans IDE. - - - - - - - - - Must select one file in the IDE or set profile.class - This target only works when run from inside the NetBeans IDE. - - - - - - - - - This target only works when run from inside the NetBeans IDE. - - - - - - - - - - - - - This target only works when run from inside the NetBeans IDE. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set run.class - - - - - - Must select some files in the IDE or set test.includes - - - - - Must select one file in the IDE or set run.class - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - - - Some tests failed; see details above. - - - - - - - - - Must select some files in the IDE or set test.includes - - - - Some tests failed; see details above. - - - - Must select some files in the IDE or set test.class - Must select some method in the IDE or set test.method - - - - Some tests failed; see details above. - - - - - Must select one file in the IDE or set test.class - - - - Must select one file in the IDE or set test.class - Must select some method in the IDE or set test.method - - - - - - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libsrc/ttf/nbproject/genfiles.properties b/libsrc/ttf/nbproject/genfiles.properties index 29b2c0e4d..32a75fe67 100644 --- a/libsrc/ttf/nbproject/genfiles.properties +++ b/libsrc/ttf/nbproject/genfiles.properties @@ -1,8 +1,8 @@ -build.xml.data.CRC32=ffc041f7 -build.xml.script.CRC32=9bce9219 -build.xml.stylesheet.CRC32=8064a381@1.68.1.46 -# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. -# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=ffc041f7 -nbproject/build-impl.xml.script.CRC32=fdde8048 -nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.74.1.48 +build.xml.data.CRC32=ffc041f7 +build.xml.script.CRC32=9bce9219 +build.xml.stylesheet.CRC32=8064a381@1.68.1.46 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=ffc041f7 +nbproject/build-impl.xml.script.CRC32=c196f24b +nbproject/build-impl.xml.stylesheet.CRC32=05530350@1.79.1.48 From 6e21a941cbdb0ba7100e374752a231560d6e4065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 17 Jul 2016 15:52:55 +0200 Subject: [PATCH 04/20] AMF3 Dictionary weakKeys attribute to String --- .../src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java | 1 + .../jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java index eb9daa7ba..c13a26114 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java @@ -218,6 +218,7 @@ public class AMF3Tools { ret.append("{\r\n"); ret.append(indent(level + 1)).append("\"type\": \"Dictionary\",\r\n"); ret.append(addId); + ret.append(indent(level + 1)).append("\"weakKeys\": " + dt.hasWeakKeys() + ",\r\n"); ret.append(indent(level + 1)).append("\"value\": {\r\n"); for (int i = 0; i < dt.getPairs().size(); i++) { Pair pair = dt.getPairs().get(i); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java index 29cf399f2..57155f719 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java @@ -35,4 +35,8 @@ public class DictionaryType implements WithSubValues { return AMF3Tools.amfToString(this); } + public boolean hasWeakKeys() { + return weakKeys; + } + } From 6500c0deb25a9c9e81a86f3a036771d8a7d37d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 17 Jul 2016 15:53:41 +0200 Subject: [PATCH 05/20] unused import --- .../src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java | 1 - 1 file changed, 1 deletion(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java index 9b0a162f9..e230c269f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java @@ -15,7 +15,6 @@ import com.jpexs.decompiler.flash.amf.amf3.types.BasicType; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.file.NoSuchFileException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; From 63cc2115d39247e43ce6291c142fc8f70d0e102c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 17 Jul 2016 15:55:32 +0200 Subject: [PATCH 06/20] rename AMF to Amf part1 --- .../amf3/{AMF3InputStream.java => Amf3InputStream_.java} | 6 +++--- .../amf3/{AMF3OutputStream.java => Amf3OutputStream_.java} | 4 ++-- .../flash/amf/amf3/{AMF3Tools.java => Amf3Tools_.java} | 2 +- .../decompiler/flash/amf/amf3/types/AbstractVectorType.java | 4 ++-- .../jpexs/decompiler/flash/amf/amf3/types/ArrayType.java | 4 ++-- .../decompiler/flash/amf/amf3/types/ByteArrayType.java | 4 ++-- .../com/jpexs/decompiler/flash/amf/amf3/types/DateType.java | 4 ++-- .../decompiler/flash/amf/amf3/types/DictionaryType.java | 4 ++-- .../jpexs/decompiler/flash/amf/amf3/types/ObjectType.java | 4 ++-- 9 files changed, 18 insertions(+), 18 deletions(-) rename libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/{AMF3InputStream.java => Amf3InputStream_.java} (99%) rename libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/{AMF3OutputStream.java => Amf3OutputStream_.java} (95%) rename libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/{AMF3Tools.java => Amf3Tools_.java} (99%) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream_.java similarity index 99% rename from libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java rename to libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream_.java index e230c269f..1ccce3d7b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3InputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream_.java @@ -22,12 +22,12 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; -public class AMF3InputStream extends InputStream { +public class Amf3InputStream_ extends InputStream { - public final static Logger LOGGER = Logger.getLogger(AMF3InputStream.class.getName()); + public final static Logger LOGGER = Logger.getLogger(Amf3InputStream_.class.getName()); private final InputStream is; - public AMF3InputStream(InputStream is) { + public Amf3InputStream_(InputStream is) { this.is = is; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3OutputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream_.java similarity index 95% rename from libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3OutputStream.java rename to libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream_.java index 0c93ce193..75e54113c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3OutputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream_.java @@ -3,11 +3,11 @@ package com.jpexs.decompiler.flash.amf.amf3; import java.io.IOException; import java.io.OutputStream; -public class AMF3OutputStream extends OutputStream { +public class Amf3OutputStream_ extends OutputStream { private final OutputStream os; - public AMF3OutputStream(OutputStream os) { + public Amf3OutputStream_(OutputStream os) { this.os = os; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Tools_.java similarity index 99% rename from libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java rename to libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Tools_.java index c13a26114..20eade30e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/AMF3Tools.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Tools_.java @@ -18,7 +18,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class AMF3Tools { +public class Amf3Tools_ { /** * Converts AMF value to something human-readable. diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java index ebdb1b2ad..5330639a5 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.AMF3Tools; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools_; import java.util.ArrayList; import java.util.List; import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; @@ -34,7 +34,7 @@ public abstract class AbstractVectorType implements WithSubValues { @Override public String toString() { - return AMF3Tools.amfToString(this); + return Amf3Tools_.amfToString(this); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java index a3caf7030..e68373bda 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.AMF3Tools; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools_; import com.jpexs.decompiler.flash.amf.amf3.Pair; import com.jpexs.helpers.Helper; import java.util.ArrayList; @@ -27,7 +27,7 @@ public class ArrayType implements WithSubValues { @Override public String toString() { - return AMF3Tools.amfToString(this); + return Amf3Tools_.amfToString(this); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java index 93cedf228..9edd350d0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.AMF3Tools; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools_; public class ByteArrayType { @@ -16,7 +16,7 @@ public class ByteArrayType { @Override public String toString() { - return AMF3Tools.amfToString(this); + return Amf3Tools_.amfToString(this); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java index 821a8a0cb..6b7ce45b3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.AMF3Tools; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools_; import java.util.Date; public class DateType { @@ -21,7 +21,7 @@ public class DateType { @Override public String toString() { - return AMF3Tools.amfToString(this); + return Amf3Tools_.amfToString(this); } public Date toDate() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java index 57155f719..e6ba28a88 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.AMF3Tools; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools_; import com.jpexs.decompiler.flash.amf.amf3.Pair; import java.util.ArrayList; import java.util.List; @@ -32,7 +32,7 @@ public class DictionaryType implements WithSubValues { @Override public String toString() { - return AMF3Tools.amfToString(this); + return Amf3Tools_.amfToString(this); } public boolean hasWeakKeys() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java index 3e0dfb4cc..a938b64a3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.AMF3Tools; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools_; import com.jpexs.decompiler.flash.amf.amf3.Pair; import com.jpexs.helpers.Helper; import java.util.ArrayList; @@ -94,7 +94,7 @@ public class ObjectType implements WithSubValues { @Override public String toString() { - return AMF3Tools.amfToString(this); + return Amf3Tools_.amfToString(this); } public void setSerializedData(byte[] serializedData) { From 74642edfe9f9e4a2981b9299349a77ec8f92a069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 17 Jul 2016 15:56:07 +0200 Subject: [PATCH 07/20] rename AMF to Amf part2 --- .../amf3/{Amf3InputStream_.java => Amf3InputStream.java} | 6 +++--- .../amf3/{Amf3OutputStream_.java => Amf3OutputStream.java} | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/{Amf3InputStream_.java => Amf3InputStream.java} (99%) rename libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/{Amf3OutputStream_.java => Amf3OutputStream.java} (95%) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream_.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java similarity index 99% rename from libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream_.java rename to libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java index 1ccce3d7b..93a5acce5 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream_.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java @@ -22,12 +22,12 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; -public class Amf3InputStream_ extends InputStream { +public class Amf3InputStream extends InputStream { - public final static Logger LOGGER = Logger.getLogger(Amf3InputStream_.class.getName()); + public final static Logger LOGGER = Logger.getLogger(Amf3InputStream.class.getName()); private final InputStream is; - public Amf3InputStream_(InputStream is) { + public Amf3InputStream(InputStream is) { this.is = is; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream_.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream.java similarity index 95% rename from libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream_.java rename to libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream.java index 75e54113c..7f86a4c85 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream_.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream.java @@ -3,11 +3,11 @@ package com.jpexs.decompiler.flash.amf.amf3; import java.io.IOException; import java.io.OutputStream; -public class Amf3OutputStream_ extends OutputStream { +public class Amf3OutputStream extends OutputStream { private final OutputStream os; - public Amf3OutputStream_(OutputStream os) { + public Amf3OutputStream(OutputStream os) { this.os = os; } From 092bfb4acbaf5a7304a5649b797bd08dd8758b4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 17 Jul 2016 15:56:41 +0200 Subject: [PATCH 08/20] rename AMF to Amf part3 --- .../flash/amf/amf3/{Amf3Tools_.java => Amf3Tools.java} | 2 +- .../decompiler/flash/amf/amf3/types/AbstractVectorType.java | 4 ++-- .../jpexs/decompiler/flash/amf/amf3/types/ArrayType.java | 4 ++-- .../decompiler/flash/amf/amf3/types/ByteArrayType.java | 4 ++-- .../com/jpexs/decompiler/flash/amf/amf3/types/DateType.java | 4 ++-- .../decompiler/flash/amf/amf3/types/DictionaryType.java | 4 ++-- .../jpexs/decompiler/flash/amf/amf3/types/ObjectType.java | 4 ++-- .../decompiler/flash/amf/amf3/Amf3InputStreamTest.java | 6 +++--- 8 files changed, 16 insertions(+), 16 deletions(-) rename libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/{Amf3Tools_.java => Amf3Tools.java} (99%) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Tools_.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Tools.java similarity index 99% rename from libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Tools_.java rename to libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Tools.java index 20eade30e..899db6ae1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Tools_.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Tools.java @@ -18,7 +18,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class Amf3Tools_ { +public class Amf3Tools { /** * Converts AMF value to something human-readable. diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java index 5330639a5..905236454 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools_; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; import java.util.ArrayList; import java.util.List; import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; @@ -34,7 +34,7 @@ public abstract class AbstractVectorType implements WithSubValues { @Override public String toString() { - return Amf3Tools_.amfToString(this); + return Amf3Tools.amfToString(this); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java index e68373bda..9c9813836 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools_; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; import com.jpexs.decompiler.flash.amf.amf3.Pair; import com.jpexs.helpers.Helper; import java.util.ArrayList; @@ -27,7 +27,7 @@ public class ArrayType implements WithSubValues { @Override public String toString() { - return Amf3Tools_.amfToString(this); + return Amf3Tools.amfToString(this); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java index 9edd350d0..2e0c57445 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools_; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; public class ByteArrayType { @@ -16,7 +16,7 @@ public class ByteArrayType { @Override public String toString() { - return Amf3Tools_.amfToString(this); + return Amf3Tools.amfToString(this); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java index 6b7ce45b3..6458b9dff 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools_; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; import java.util.Date; public class DateType { @@ -21,7 +21,7 @@ public class DateType { @Override public String toString() { - return Amf3Tools_.amfToString(this); + return Amf3Tools.amfToString(this); } public Date toDate() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java index e6ba28a88..6b2cc4cb2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools_; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; import com.jpexs.decompiler.flash.amf.amf3.Pair; import java.util.ArrayList; import java.util.List; @@ -32,7 +32,7 @@ public class DictionaryType implements WithSubValues { @Override public String toString() { - return Amf3Tools_.amfToString(this); + return Amf3Tools.amfToString(this); } public boolean hasWeakKeys() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java index a938b64a3..c856e9366 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools_; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; import com.jpexs.decompiler.flash.amf.amf3.Pair; import com.jpexs.helpers.Helper; import java.util.ArrayList; @@ -94,7 +94,7 @@ public class ObjectType implements WithSubValues { @Override public String toString() { - return Amf3Tools_.amfToString(this); + return Amf3Tools.amfToString(this); } public void setSerializedData(byte[] serializedData) { diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java index 7a83cb0a5..8b23694cc 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java @@ -21,7 +21,7 @@ import org.testng.annotations.Test; public class Amf3InputStreamTest { private FileInputStream fis; - private AMF3InputStream is; + private Amf3InputStream is; @AfterTest public void deinitStream() { @@ -45,7 +45,7 @@ public class Amf3InputStreamTest { private void initStream(String fileName) throws FileNotFoundException { fis = new FileInputStream("testdata/amf3/generated/" + fileName); - is = new AMF3InputStream(fis); + is = new Amf3InputStream(fis); } @Test @@ -165,7 +165,7 @@ public class Amf3InputStreamTest { @Test(expectedExceptions = UnsupportedValueType.class) public void testUnsupportedMarker() throws IOException, NoSerializerExistsException { final int UNSUPPORTED_MARKER = 100; - is = new AMF3InputStream(new ByteArrayInputStream(new byte[]{UNSUPPORTED_MARKER})); + is = new Amf3InputStream(new ByteArrayInputStream(new byte[]{UNSUPPORTED_MARKER})); is.readValue(); } } From d33c8e8c32e43017567eee2e058fea586014acf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 17 Jul 2016 19:05:50 +0200 Subject: [PATCH 09/20] writing AMF3 + tests traits handling fix empty string handling fix --- .../flash/amf/amf3/Amf3InputStream.java | 26 +- .../flash/amf/amf3/Amf3OutputStream.java | 320 +++++++++++++++++- .../flash/amf/amf3/UnsupportedValueType.java | 16 - .../amf3/UnsupportedValueTypeException.java | 26 ++ .../flash/amf/amf3/types/ObjectType.java | 37 +- .../flash/amf/amf3/Amf3InputStreamTest.java | 4 +- .../flash/amf/amf3/Amf3OutputStreamTest.java | 98 ++++++ 7 files changed, 454 insertions(+), 73 deletions(-) delete mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/UnsupportedValueType.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/UnsupportedValueTypeException.java create mode 100644 libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java index 93a5acce5..38057997a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java @@ -115,8 +115,11 @@ public class Amf3InputStream extends InputStream { int stringNoRefFlag = (int) (u & 1); if (stringNoRefFlag == 1) { int byteLength = (int) (u >> 1); //TODO: long strings, int is not enough for them - String retString = readUtf8Char(byteLength); - stringTable.add(retString); + String retString = ""; + if (byteLength > 0) { + retString = readUtf8Char(byteLength); + stringTable.add(retString); + } LOGGER.log(Level.FINE, "Read string: \"{0}\"", retString); return retString; } else { //flag==0 @@ -288,13 +291,14 @@ public class Amf3InputStream extends InputStream { if (objectTraitsExtFlag == 1) { String className = readUtf8Vr(stringTable); if (!serializers.containsKey(className)) { - throw new NoSerializerExistsException(className, new ObjectType(className, null, new ArrayList<>()), null); + throw new NoSerializerExistsException(className, new ObjectType(new Traits(className, false, new ArrayList<>()), (byte[]) null, new ArrayList<>()), null); } MonitoredInputStream mis = new MonitoredInputStream(is); List> serMembers = serializers.get(className).readObject(className, mis); byte serData[] = mis.getReadData(); - retObjectType = new ObjectType(className, serData, serMembers); + Traits unserTraits = new Traits(className, false, new ArrayList<>()); + retObjectType = new ObjectType(unserTraits, serData, serMembers); LOGGER.log(Level.FINER, "Object/Traits value: customSerialized"); objectTable.add(retObjectType); @@ -312,20 +316,16 @@ public class Amf3InputStream extends InputStream { } traits = new Traits(className, dynamicFlag == 1, sealedMemberNames); } - + traitsTable.add(traits); } else { int refIndexTraits = (int) (objectU29 >> 2); traits = traitsTable.get(refIndexTraits); LOGGER.log(Level.FINER, "Traits value: reference({0}) - traitsize={1}", new Object[]{refIndexTraits, traits.getSealedMemberNames().size()}); } - - if (objectTraitsNoRefFlag == 1) { - traitsTable.add(traits); - } List> sealedMembers = new ArrayList<>(); List> dynamicMembers = new ArrayList<>(); - Object retObjectType = new ObjectType(traits.isDynamic(), sealedMembers, dynamicMembers, traits.getClassName()); + Object retObjectType = new ObjectType(traits, sealedMembers, dynamicMembers); objectTable.add(retObjectType); //add it before any subvalue can reference it List sealedMemberValues = new ArrayList<>(); NoSerializerExistsException error = null; @@ -503,6 +503,8 @@ public class Amf3InputStream extends InputStream { int numEntries = (int) (dictionaryObjectU29 >> 1); int weakKeys = readU8(); List> data = new ArrayList<>(); + DictionaryType retDictionary = new DictionaryType(weakKeys == 1, data); + objectTable.add(retDictionary); NoSerializerExistsException error = null; for (int i = 0; i < numEntries; i++) { Object key; @@ -529,8 +531,6 @@ public class Amf3InputStream extends InputStream { break; } } - DictionaryType retDictionary = new DictionaryType(weakKeys == 1, data); - objectTable.add(retDictionary); if (error != null) { throw new NoSerializerExistsException(error.getClassName(), retDictionary, error); } @@ -541,7 +541,7 @@ public class Amf3InputStream extends InputStream { return objectTable.get(refIndexDictionary); } default: - throw new UnsupportedValueType(marker); + throw new UnsupportedValueTypeException(marker); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream.java index 7f86a4c85..39371a5ae 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStream.java @@ -1,28 +1,65 @@ package com.jpexs.decompiler.flash.amf.amf3; +import com.jpexs.decompiler.flash.amf.amf3.types.ArrayType; +import com.jpexs.decompiler.flash.amf.amf3.types.BasicType; +import com.jpexs.decompiler.flash.amf.amf3.types.ByteArrayType; +import com.jpexs.decompiler.flash.amf.amf3.types.DateType; +import com.jpexs.decompiler.flash.amf.amf3.types.DictionaryType; +import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorDoubleType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorIntType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorObjectType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorUIntType; +import com.jpexs.decompiler.flash.amf.amf3.types.XmlDocType; +import com.jpexs.decompiler.flash.amf.amf3.types.XmlType; import java.io.IOException; import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; public class Amf3OutputStream extends OutputStream { + public final static Logger LOGGER = Logger.getLogger(Amf3OutputStream.class.getName()); + private final OutputStream os; + private static final int NO_REFERENCE_FLAG = 1; + + private static final int NO_TRAIT_REFERENCE_FLAG = 2; + private static final int TRAIT_EXT_FLAG = 4; + private static final int DYNAMIC_FLAG = 8; public Amf3OutputStream(OutputStream os) { this.os = os; } - public void writeUI8(int v) throws IOException { + public void writeU8(int v) throws IOException { write(v); } - public void writeUI16(int v) throws IOException { + public void writeU16(int v) throws IOException { int b1 = (v >> 8) & 0xff; int b2 = v & 0xff; write(b1); write(b2); } - public void writeUI32(long v) throws IOException { + private void writeLong(long value) throws IOException { + byte[] writeBuffer = new byte[8]; + writeBuffer[0] = (byte) (value >>> 56); + writeBuffer[1] = (byte) (value >>> 48); + writeBuffer[2] = (byte) (value >>> 40); + writeBuffer[3] = (byte) (value >>> 32); + writeBuffer[4] = (byte) (value >>> 24); + writeBuffer[5] = (byte) (value >>> 16); + writeBuffer[6] = (byte) (value >>> 8); + writeBuffer[7] = (byte) (value); + write(writeBuffer); + } + + public void writeU32(long v) throws IOException { int b1 = (int) ((v >> 24) & 0xff); int b2 = (int) ((v >> 16) & 0xff); int b3 = (int) ((v >> 8) & 0xff); @@ -35,29 +72,39 @@ public class Amf3OutputStream extends OutputStream { } public void writeDouble(double v) throws IOException { - writeUI32(Double.doubleToLongBits(v)); + writeLong(Double.doubleToLongBits(v)); } - public void writeUI29(long v) throws IOException { + public void writeBytes(byte[] data) throws IOException { + os.write(data); + } + + public void writeU29(long v) throws IOException { + v = v & 0x3FFFFFFF; //make unsigned + + final int USE_NEXT_BYTE_FLAG = 0x80; + final int SEVEN_BITS_MASK = 0x7f; + final int EIGHT_BITS_MASK = 0xff; + if (v <= 0x7F) { write((int) v); } else if (v <= 0x3FFF) { - int b1 = (int) ((v >> 7) & 0x7F); - int b2 = (int) (v & 0x7F); + int b1 = (int) ((v >> 7) & SEVEN_BITS_MASK) | USE_NEXT_BYTE_FLAG; + int b2 = (int) (v & SEVEN_BITS_MASK); write(b1); write(b2); } else if (v <= 0x1FFFFF) { - int b1 = (int) ((v >> 14) & 0x7F); - int b2 = (int) ((v >> 7) & 0x7F); - int b3 = (int) (v & 0x7F); + int b1 = (int) ((v >> 14) & SEVEN_BITS_MASK) | USE_NEXT_BYTE_FLAG; + int b2 = (int) ((v >> 7) & SEVEN_BITS_MASK) | USE_NEXT_BYTE_FLAG; + int b3 = (int) (v & SEVEN_BITS_MASK); write(b1); write(b2); write(b3); } else if (v <= 0x3FFFFFFF) { - int b1 = (int) ((v >> 21) & 0x7F); - int b2 = (int) ((v >> 14) & 0x7F); - int b3 = (int) ((v >> 7) & 0x7F); - int b4 = (int) (v & 0x7F); + int b1 = (int) ((v >> 21) & SEVEN_BITS_MASK) | USE_NEXT_BYTE_FLAG; + int b2 = (int) ((v >> 14) & SEVEN_BITS_MASK) | USE_NEXT_BYTE_FLAG; + int b3 = (int) ((v >> 7) & SEVEN_BITS_MASK) | USE_NEXT_BYTE_FLAG; + int b4 = (int) (v & EIGHT_BITS_MASK); write(b1); write(b2); write(b3); @@ -67,13 +114,252 @@ public class Amf3OutputStream extends OutputStream { } } + private void writeUtf8Vr(String val, List stringTable) throws IOException { + int stringIndex = stringTable.indexOf(val); + if (stringIndex == -1) { + if (!val.isEmpty()) { + stringTable.add(val); + } + byte data[] = val.getBytes("UTF-8"); + writeU29((data.length << 1) | NO_REFERENCE_FLAG); + writeBytes(data); + } else { + writeU29((stringIndex << 1)); + } + } + + private void writeByteArray(ByteArrayType val, List objectTable) throws IOException { + int objectIndex = objectTable.indexOf(val); + if (objectIndex == -1) { + objectTable.add(val); + byte data[] = val.getData(); + writeU29((data.length << 1) | NO_REFERENCE_FLAG); + writeBytes(data); + } else { + writeU29((objectIndex << 1)); + } + } + + private void writeXmlDoc(XmlDocType val, List objectTable) throws IOException { + int objectIndex = objectTable.indexOf(val); + if (objectIndex == -1) { + objectTable.add(val); + byte data[] = val.getData().getBytes("UTF-8"); + writeU29((data.length << 1) | NO_REFERENCE_FLAG); + writeBytes(data); + } else { + writeU29((objectIndex << 1)); + } + } + + private void writeXml(XmlType val, List objectTable) throws IOException { + int objectIndex = objectTable.indexOf(val); + if (objectIndex == -1) { + objectTable.add(val); + byte data[] = val.getData().getBytes("UTF-8"); + writeU29((data.length << 1) | NO_REFERENCE_FLAG); + writeBytes(data); + } else { + writeU29((objectIndex << 1)); + } + } + @Override public void write(int v) throws IOException { os.write(v); } - public void writeValue(Object object) throws IOException { - //TODO:!!! - throw new UnsupportedOperationException("Not implemented yet"); + private void writeArray(ArrayType val, Map serializers, List stringTable, List traitsTable, List objectTable) throws IOException, NoSerializerExistsException { + int objectIndex = objectTable.indexOf(val); + if (objectIndex == -1) { + objectTable.add(val); + + writeU29((val.getDenseValues().size() << 1) | NO_REFERENCE_FLAG); + for (Pair p : val.getAssociativeValues()) { + writeUtf8Vr(p.getFirst(), stringTable); + writeValue(p.getSecond(), serializers, stringTable, traitsTable, objectTable); + } + writeUtf8Vr("", stringTable); + for (Object v : val.getDenseValues()) { + writeValue(v, serializers, stringTable, traitsTable, objectTable); + } + } else { + writeU29((objectIndex << 1)); + } + } + + private void writeObject(ObjectType val, Map serializers, List stringTable, List traitsTable, List objectTable) throws IOException, NoSerializerExistsException { + int objectIndex = objectTable.indexOf(val); + if (objectIndex == -1) { + objectTable.add(val); + Traits traits = val.getTraits(); + int traitsIndex = traitsTable.indexOf(traits); + if (traitsIndex == -1) { + if (val.isSerialized()) { + writeU29(NO_REFERENCE_FLAG | NO_TRAIT_REFERENCE_FLAG | TRAIT_EXT_FLAG); + writeUtf8Vr(val.getClassName(), stringTable); + if (serializers.containsKey(val.getClassName())) { + serializers.get(val.getClassName()).writeObject(val.getSerializedMembers(), os); + } else if (val.getSerializedData() != null) { + writeBytes(val.getSerializedData()); + } else { + throw new NoSerializerExistsException(val.getClassName(), null, null); + } + } else { + traitsTable.add(traits); + writeU29((val.getSealedMembers().size() << 4) | NO_REFERENCE_FLAG | NO_TRAIT_REFERENCE_FLAG | (traits.isDynamic() ? DYNAMIC_FLAG : 0)); + writeUtf8Vr(val.getClassName(), stringTable); + for (Pair v : val.getSealedMembers()) { + writeUtf8Vr(v.getFirst(), stringTable); + } + } + } else { + writeU29((traitsIndex << 2) | NO_REFERENCE_FLAG); + } + for (Pair v : val.getSealedMembers()) { + writeValue(v.getSecond(), serializers, stringTable, traitsTable, objectTable); + } + if (traits.isDynamic()) { + for (Pair v : val.getDynamicMembers()) { + writeUtf8Vr(v.getFirst(), stringTable); + writeValue(v.getSecond(), serializers, stringTable, traitsTable, objectTable); + } + writeUtf8Vr("", stringTable); + } + } else { + writeU29((objectIndex << 1)); + } + } + + public void writeValue(Object object) throws IOException, NoSerializerExistsException { + writeValue(object, new HashMap<>()); + } + + public void writeValue(Object object, Map serializers) throws IOException, NoSerializerExistsException { + writeValue(object, serializers, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + } + + private void writeValue(Object object, Map serializers, List stringTable, List traitsTable, List objectTable) throws IOException, NoSerializerExistsException { + if (object == BasicType.UNDEFINED) { + writeU8(Marker.UNDEFINED); + } else if (object == BasicType.NULL) { + writeU8(Marker.NULL); + } else if (object == Boolean.FALSE) { + writeU8(Marker.FALSE); + } else if (object == Boolean.TRUE) { + writeU8(Marker.TRUE); + } else if (object == BasicType.UNKNOWN) { + //Nothing + } else if (object instanceof Long) { + writeU8(Marker.INTEGER); + writeU29((Long) object); + } else if (object instanceof Double) { + writeU8(Marker.DOUBLE); + writeDouble((Double) object); + } else if (object instanceof String) { + writeU8(Marker.STRING); + writeUtf8Vr((String) object, stringTable); + } else if (object instanceof XmlDocType) { + writeU8(Marker.XML_DOC); + writeXmlDoc((XmlDocType) object, objectTable); + } else if (object instanceof DateType) { + writeU8(Marker.DATE); + int dateIndex = objectTable.indexOf(object); + DateType val = (DateType) object; + if (dateIndex == -1) { + objectTable.add(val); + writeU29(NO_REFERENCE_FLAG); + writeDouble(val.getVal()); + } else { + writeU29(dateIndex << 1); + } + } else if (object instanceof ArrayType) { + writeU8(Marker.ARRAY); + writeArray((ArrayType) object, serializers, stringTable, traitsTable, objectTable); + } else if (object instanceof ObjectType) { + writeU8(Marker.OBJECT); + writeObject((ObjectType) object, serializers, stringTable, traitsTable, objectTable); + } else if (object instanceof XmlType) { + writeU8(Marker.XML); + writeXml((XmlType) object, objectTable); + } else if (object instanceof ByteArrayType) { + writeU8(Marker.BYTE_ARRAY); + writeByteArray((ByteArrayType) object, objectTable); + } else if (object instanceof VectorIntType) { + writeU8(Marker.VECTOR_INT); + + int vectorIndex = objectTable.indexOf(object); + VectorIntType val = (VectorIntType) object; + if (vectorIndex == -1) { + objectTable.add(val); + writeU29((val.getValues().size() << 1) | NO_REFERENCE_FLAG); + writeU8(val.isFixed() ? 1 : 0); + for (long v : val.getValues()) { + writeU32(v); + } + } else { + writeU29(vectorIndex << 1); + } + } else if (object instanceof VectorUIntType) { + writeU8(Marker.VECTOR_UINT); + int vectorIndex = objectTable.indexOf(object); + VectorUIntType val = (VectorUIntType) object; + if (vectorIndex == -1) { + objectTable.add(val); + writeU29((val.getValues().size() << 1) | NO_REFERENCE_FLAG); + writeU8(val.isFixed() ? 1 : 0); + for (long v : val.getValues()) { + writeU32(v); + } + } else { + writeU29(vectorIndex << 1); + } + } else if (object instanceof VectorDoubleType) { + writeU8(Marker.VECTOR_DOUBLE); + int vectorIndex = objectTable.indexOf(object); + VectorDoubleType val = (VectorDoubleType) object; + if (vectorIndex == -1) { + objectTable.add(val); + writeU29((val.getValues().size() << 1) | NO_REFERENCE_FLAG); + writeU8(val.isFixed() ? 1 : 0); + for (double v : val.getValues()) { + writeDouble(v); + } + } else { + writeU29(vectorIndex << 1); + } + } else if (object instanceof VectorObjectType) { + writeU8(Marker.VECTOR_OBJECT); + int vectorIndex = objectTable.indexOf(object); + VectorObjectType val = (VectorObjectType) object; + if (vectorIndex == -1) { + objectTable.add(val); + writeU29((val.getValues().size() << 1) | NO_REFERENCE_FLAG); + writeU8(val.isFixed() ? 1 : 0); + writeUtf8Vr(val.getTypeName(), stringTable); + for (Object v : val.getValues()) { + writeValue(v, serializers, stringTable, traitsTable, objectTable); + } + } else { + writeU29(vectorIndex << 1); + } + } else if (object instanceof DictionaryType) { + writeU8(Marker.DICTIONARY); + int dictionaryIndex = objectTable.indexOf(object); + DictionaryType val = (DictionaryType) object; + if (dictionaryIndex == -1) { + objectTable.add(val); + writeU29((val.getPairs().size() << 1) | NO_REFERENCE_FLAG); + writeU8(val.hasWeakKeys() ? 1 : 0); + for (Pair p : val.getPairs()) { + writeValue(p.getFirst(), serializers, stringTable, traitsTable, objectTable); + writeValue(p.getSecond(), serializers, stringTable, traitsTable, objectTable); + } + } else { + writeU29(dictionaryIndex << 1); + } + } else { + throw new UnsupportedValueTypeException(object.getClass()); + } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/UnsupportedValueType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/UnsupportedValueType.java deleted file mode 100644 index 1228a4cd1..000000000 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/UnsupportedValueType.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.jpexs.decompiler.flash.amf.amf3; - -public class UnsupportedValueType extends RuntimeException { - - private int marker; - - public UnsupportedValueType(int marker) { - super("Unsupported type of value - marker: 0x" + Integer.toHexString(marker)); - this.marker = marker; - } - - public int getMarker() { - return marker; - } - -} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/UnsupportedValueTypeException.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/UnsupportedValueTypeException.java new file mode 100644 index 000000000..30717768e --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/UnsupportedValueTypeException.java @@ -0,0 +1,26 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +public class UnsupportedValueTypeException extends RuntimeException { + + private Integer marker = null; + private Class cls = null; + + public UnsupportedValueTypeException(Class cls) { + super("Unsupported type of value - class: " + cls.getSimpleName()); + this.cls = cls; + } + + public UnsupportedValueTypeException(int marker) { + super("Unsupported type of value - marker: 0x" + Integer.toHexString(marker)); + this.marker = marker; + } + + public Integer getMarker() { + return marker; + } + + public Class getUnsupportedClass() { + return this.cls; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java index c856e9366..fd0deb2ff 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java @@ -2,6 +2,7 @@ package com.jpexs.decompiler.flash.amf.amf3.types; import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; import com.jpexs.decompiler.flash.amf.amf3.Pair; +import com.jpexs.decompiler.flash.amf.amf3.Traits; import com.jpexs.helpers.Helper; import java.util.ArrayList; import java.util.List; @@ -9,21 +10,24 @@ import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; public class ObjectType implements WithSubValues { - private boolean dynamic; private List> sealedMembers; private List> dynamicMembers; private List> serializedMembers; - private String className; //null = not serialized or unknown private byte[] serializedData = null; private boolean serialized; + private Traits traits; public boolean isSerialized() { return serialized; } - public ObjectType(String className, byte[] serializedData, List> serializedMembers) { - this.className = className; + public Traits getTraits() { + return traits; + } + + public ObjectType(Traits traits, byte[] serializedData, List> serializedMembers) { + this.traits = traits; this.serializedData = serializedData; this.serializedMembers = serializedMembers; this.dynamicMembers = new ArrayList<>(); @@ -31,17 +35,16 @@ public class ObjectType implements WithSubValues { this.serialized = true; } - public ObjectType(boolean dynamic, List> sealedMembers, List> dynamicMembers, String className) { - this.dynamic = dynamic; + public ObjectType(Traits traits, List> sealedMembers, List> dynamicMembers) { this.sealedMembers = sealedMembers; this.dynamicMembers = dynamicMembers; - this.className = className; this.serializedMembers = new ArrayList<>(); this.serialized = false; + this.traits = traits; } public boolean isDynamic() { - return dynamic; + return traits.isDynamic(); } public List> getDynamicMembers() { @@ -53,23 +56,7 @@ public class ObjectType implements WithSubValues { } public String getClassName() { - return className; - } - - public void setDynamic(boolean dynamic) { - this.dynamic = dynamic; - } - - public void setDynamicMembers(List> dynamicMembers) { - this.dynamicMembers = dynamicMembers; - } - - public void setSealedMembers(List> sealedMembers) { - this.sealedMembers = sealedMembers; - } - - public void setClassName(String className) { - this.className = className; + return traits.getClassName(); } @Override diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java index 8b23694cc..1f5104119 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java @@ -67,7 +67,7 @@ public class Amf3InputStreamTest { @Override public void writeObject(List> members, OutputStream os) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); + throw new UnsupportedOperationException("Not implemented"); } }); return serializers; @@ -162,7 +162,7 @@ public class Amf3InputStreamTest { } } - @Test(expectedExceptions = UnsupportedValueType.class) + @Test(expectedExceptions = UnsupportedValueTypeException.class) public void testUnsupportedMarker() throws IOException, NoSerializerExistsException { final int UNSUPPORTED_MARKER = 100; is = new Amf3InputStream(new ByteArrayInputStream(new byte[]{UNSUPPORTED_MARKER})); diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java new file mode 100644 index 000000000..23f2fd0fc --- /dev/null +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java @@ -0,0 +1,98 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class Amf3OutputStreamTest { + + private Map getSerializers() { + Map serializers = new HashMap<>(); + serializers.put("CustomClass", new ObjectTypeSerializeHandler() { + @Override + public List> readObject(String className, InputStream is) throws IOException { + List> members = new ArrayList<>(); + members.add(new Pair<>("val8", (long) is.read())); + members.add(new Pair<>("val32", (long) ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read())))); + return members; + } + + @Override + public void writeObject(List> members, OutputStream os) throws IOException { + Map memberMap = new HashMap<>(); + for (Pair m : members) { + memberMap.put(m.getFirst(), m.getSecond()); + } + os.write((int) (long) (Long) memberMap.get("val8")); + long val32 = (long) (Long) memberMap.get("val32"); + os.write((int) ((val32 >> 24) & 0xff)); + os.write((int) ((val32 >> 16) & 0xff)); + os.write((int) ((val32 >> 8) & 0xff)); + os.write((int) (val32 & 0xff)); + } + }); + return serializers; + } + + @DataProvider(name = "files") + public static Object[][] provideSamples() { + return new Object[][]{ + {"all.bin"}, + {"custom.bin"}, + {"noserializer_array_associative.bin"}, + {"noserializer_array_dense.bin"}, + {"noserializer_dictionary_key.bin"}, + {"noserializer_dictionary_value.bin"}, + {"noserializer_object_dynamic.bin"}, + {"noserializer_object_sealed.bin"}, + {"noserializer_vector.bin"} + }; + } + + private static byte[] readFile(File file) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte buf[] = new byte[1024]; + int cnt; + try (FileInputStream fis = new FileInputStream(file)) { + while ((cnt = fis.read(buf)) > 0) { + baos.write(buf, 0, cnt); + } + } + return baos.toByteArray(); + } + + @Test(dataProvider = "files") + public void testRecompile(String fileName) throws FileNotFoundException, IOException, NoSerializerExistsException { + + File originalFile = new File("testdata/amf3/generated/" + fileName); + + byte[] originalData = readFile(originalFile); + byte[] savedData; + try (FileInputStream fis = new FileInputStream(originalFile)) { + Amf3InputStream is = new Amf3InputStream(fis); + + Object val = is.readValue(getSerializers()); + File savedFile = new File("testdata/amf3/generated/recompiled." + fileName); + try (FileOutputStream fos = new FileOutputStream(savedFile)) { + Amf3OutputStream os = new Amf3OutputStream(fos); + os.writeValue(val, getSerializers()); + } + savedData = readFile(savedFile); + savedFile.delete(); + } + + Assert.assertEquals(savedData, originalData); + } +} From f322581a31faa31cfab3989d55876e58e16877ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Mon, 18 Jul 2016 08:37:55 +0200 Subject: [PATCH 10/20] DumpInfo in AMF3 PlaceObject4 read/write AMF data --- .../decompiler/flash/SWFInputStream.java | 19 + .../decompiler/flash/SWFOutputStream.java | 32 + .../flash/amf/amf3/Amf3InputStream.java | 909 ++++++++++-------- .../flash/tags/PlaceObject4Tag.java | 22 +- .../flash/amf/amf3/Amf3InputStreamTest.java | 42 +- .../flash/amf/amf3/Amf3OutputStreamTest.java | 26 +- 6 files changed, 629 insertions(+), 421 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java index 55f25e910..eccdbad62 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java @@ -118,6 +118,8 @@ import com.jpexs.decompiler.flash.action.swf7.ActionExtends; import com.jpexs.decompiler.flash.action.swf7.ActionImplementsOp; import com.jpexs.decompiler.flash.action.swf7.ActionThrow; import com.jpexs.decompiler.flash.action.swf7.ActionTry; +import com.jpexs.decompiler.flash.amf.amf3.Amf3InputStream; +import com.jpexs.decompiler.flash.amf.amf3.NoSerializerExistsException; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.dumpview.DumpInfo; import com.jpexs.decompiler.flash.tags.CSMTextSettingsTag; @@ -758,6 +760,23 @@ public class SWFInputStream implements AutoCloseable { return ret; } + /** + * Reads AMF3 encoded value from the stream + * + * @param name + * @return + * @throws IOException + */ + public Object readAmf3Object(String name) throws IOException { + Amf3InputStream ai = new Amf3InputStream(is); + ai.dumpInfo = this.dumpInfo; + try { + return ai.readValue("amfData"); + } catch (NoSerializerExistsException nse) { + return nse.getIncompleteData(); + } + } + /** * Reads byte range from the stream * diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java index fd976b55b..41296c298 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java @@ -16,6 +16,9 @@ */ package com.jpexs.decompiler.flash; +import com.jpexs.decompiler.flash.amf.amf3.Amf3OutputStream; +import com.jpexs.decompiler.flash.amf.amf3.NoSerializerExistsException; +import com.jpexs.decompiler.flash.amf.amf3.ObjectTypeSerializeHandler; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; import com.jpexs.decompiler.flash.tags.Tag; @@ -81,7 +84,9 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.Deflater; @@ -1946,4 +1951,31 @@ public class SWFOutputStream extends OutputStream { writeUB(5, (value >> 11) & 0xff); writeUB(5, (value >> 3) & 0xff); } + + /** + * Writes AMF3 encoded value. Warning: Correct serializer needs to be passed + * as second parameter when using IExternalizable + * + * @param value + * @param serializers Map className=>Serializer for classes implementing + * IExternalizable + * @throws IOException + * @throws NoSerializerExistsException + */ + public void writeAmf3Object(Object value, Map serializers) throws IOException, NoSerializerExistsException { + Amf3OutputStream ao = new Amf3OutputStream(os); + ao.writeValue(value, serializers); + } + + /** + * Writes AMF3 encoded value. Warning: When the object implements + * IExternalizable, you need to pass serializer as second parameter + * + * @param value + * @throws IOException + * @throws NoSerializerExistsException + */ + public void writeAmf3Object(Object value) throws IOException, NoSerializerExistsException { + writeAmf3Object(value, new HashMap<>()); + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java index 38057997a..2134f9584 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java @@ -12,6 +12,8 @@ import com.jpexs.decompiler.flash.amf.amf3.types.VectorDoubleType; import com.jpexs.decompiler.flash.amf.amf3.types.DictionaryType; import com.jpexs.decompiler.flash.amf.amf3.types.VectorUIntType; import com.jpexs.decompiler.flash.amf.amf3.types.BasicType; +import com.jpexs.decompiler.flash.dumpview.DumpInfo; +import com.jpexs.helpers.MemoryInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -25,35 +27,98 @@ import java.util.logging.Logger; public class Amf3InputStream extends InputStream { public final static Logger LOGGER = Logger.getLogger(Amf3InputStream.class.getName()); - private final InputStream is; + private final MemoryInputStream is; + public DumpInfo dumpInfo; - public Amf3InputStream(InputStream is) { + public Amf3InputStream(MemoryInputStream is) { this.is = is; } - public int readU8() throws IOException { - return safeRead(); + public DumpInfo newDumpLevel(String name, String type) { + if (dumpInfo != null) { + long startByte = is.getPos(); + DumpInfo di = new DumpInfo(name, type, null, startByte, 0, 0, 0); + di.parent = dumpInfo; + dumpInfo.getChildInfos().add(di); + dumpInfo = di; + } + + return dumpInfo; } - public int readU16() throws IOException { - int b1 = safeRead(); - int b2 = safeRead(); - return (b1 << 8) + b2; + public void endDumpLevel() { + endDumpLevel(null); } - public long readU32() throws IOException { - int b1 = safeRead(); - int b2 = safeRead(); - int b3 = safeRead(); - int b4 = safeRead(); + public void endDumpLevel(Object value) { + if (dumpInfo != null) { + dumpInfo.lengthBytes = is.getPos() - dumpInfo.startByte; + dumpInfo.previewValue = value; + dumpInfo = dumpInfo.parent; + } + } - return (b1 << 24) + (b2 << 16) + (b3 << 8) + b4; + private void renameLastDump(String newname) { + if (dumpInfo != null) { + if (!dumpInfo.getChildInfos().isEmpty()) { + DumpInfo di = dumpInfo.getChildInfos().get(dumpInfo.getChildInfos().size() - 1); + di.name = newname; + } + } + + } + + public void endDumpLevelUntil(DumpInfo di) { + if (di != null) { + while (dumpInfo != null && dumpInfo != di) { + endDumpLevel(); + } + } + } + + public int readU8(String name) throws IOException { + newDumpLevel(name, "U8"); + int ret = readInternal(); + endDumpLevel(ret); + return ret; + } + + public int readU16(String name) throws IOException { + newDumpLevel(name, "U16"); + int b1 = readInternal(); + int b2 = readInternal(); + int ret = (b1 << 8) + b2; + endDumpLevel(ret); + return ret; + } + + public long readU32(String name) throws IOException { + newDumpLevel(name, "U32"); + long ret = readU32Internal(); + endDumpLevel(ret); + return ret; + } + + private long readU32Internal() throws IOException { + int b1 = readInternal(); + int b2 = readInternal(); + int b3 = readInternal(); + int b4 = readInternal(); + + return ((b1 << 24) + (b2 << 16) + (b3 << 8) + b4) & 0xffffffff; + } + + public long readS32(String name) throws IOException { + newDumpLevel(name, "S32"); + long ret = signExtend(readU32Internal(), 32); + endDumpLevel(ret); + return ret; } private long readLong() throws IOException { byte[] readBuffer = new byte[8]; for (int i = 0; i < 8; i++) { - readBuffer[i] = (byte) safeRead(); + readBuffer[i] = (byte) readInternal(); } return (((long) readBuffer[0] << 56) + ((long) (readBuffer[1] & 0xff) << 48) @@ -65,16 +130,32 @@ public class Amf3InputStream extends InputStream { + ((readBuffer[7] & 0xff))); } - public double readDouble() throws IOException { + public double readDouble(String name) throws IOException { + newDumpLevel(name, "DOUBLE"); long lval = readLong(); double ret = Double.longBitsToDouble(lval); + endDumpLevel(ret); return ret; } - public long readU29() throws IOException { + public long readU29(String name) throws IOException { + newDumpLevel(name, "U29"); + long val = readU29Internal(); + endDumpLevel(val); + return val; + } + + public long readS29(String name) throws IOException { + newDumpLevel(name, "S29"); + long val = signExtend(readU29Internal(), 29); + endDumpLevel(val); + return val; + } + + public long readU29Internal() throws IOException { long val = 0; for (int i = 1; i <= 4; i++) { - int b = safeRead(); + int b = readInternal(); if (i == 4) { val = ((val << 8) + b); } else { @@ -89,7 +170,7 @@ public class Amf3InputStream extends InputStream { private long signExtend(long val, int size) { if (((val >> (size - 1)) & 1) == 1) { //has sign bit - long mask = (1 << size) - 1; // 111111...up to size + long mask = size == 32 ? 0xFFFFFFFF : (1 << size) - 1; // 111111...up to size long positiveVal = (~(val - 1)) & mask; long negativeVal = -positiveVal; return negativeVal; @@ -97,32 +178,36 @@ public class Amf3InputStream extends InputStream { return val; } - private String readUtf8Char(int byteLength) throws IOException { + private String readUtf8Char(String name, long byteLength) throws IOException { if (byteLength == 0) { return ""; } + newDumpLevel(name, "UTF8-char"); + byte buf[] = new byte[(int) byteLength]; //how about long strings(?), will the int length be enough? int cnt = is.read(buf); if (cnt < buf.length) { throw new PrematureEndOfTheStreamException(); } String retString = new String(buf, "UTF-8"); + endDumpLevel(retString); return retString; } - public String readUtf8Vr(List stringTable) throws IOException { - long u = readU29(); + public String readUtf8Vr(String name, List stringTable) throws IOException { + long u = readU29("U29S"); int stringNoRefFlag = (int) (u & 1); if (stringNoRefFlag == 1) { - int byteLength = (int) (u >> 1); //TODO: long strings, int is not enough for them - String retString = ""; + renameLastDump("U29S-value"); + long byteLength = u >> 1; //TODO: long strings, int is not enough for them + String retString = readUtf8Char("characters", byteLength); if (byteLength > 0) { - retString = readUtf8Char(byteLength); stringTable.add(retString); } LOGGER.log(Level.FINE, "Read string: \"{0}\"", retString); return retString; } else { //flag==0 + renameLastDump("U29S-ref"); int stringRefTableIndex = (int) (u >> 1); String retString = stringTable.get(stringRefTableIndex); @@ -132,7 +217,7 @@ public class Amf3InputStream extends InputStream { } } - private int safeRead() throws IOException { + private int readInternal() throws IOException { int ret = read(); if (ret == -1) { throw new PrematureEndOfTheStreamException(); @@ -145,404 +230,466 @@ public class Amf3InputStream extends InputStream { return is.read(); } - public Object readValue() throws IOException, NoSerializerExistsException { - return readValue(new HashMap<>()); + public Object readValue(String name) throws IOException, NoSerializerExistsException { + return readValue(name, new HashMap<>()); } - public Object readValue(Map serializers) throws IOException, NoSerializerExistsException { - return readValue(serializers, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + public Object readValue(String name, Map serializers) throws IOException, NoSerializerExistsException { + return readValue(name, serializers, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); } - private Object readValue(Map serializers, + private Object readValue(String name, Map serializers, List objectTable, List traitsTable, List stringTable ) throws IOException, NoSerializerExistsException { + newDumpLevel(name, "value-type"); + Object result; - int marker = readU8(); - switch (marker) { - case Marker.UNDEFINED: - LOGGER.log(Level.FINE, "Read value: undefined"); - return BasicType.UNDEFINED; - case Marker.NULL: - LOGGER.log(Level.FINE, "Read value: null"); - return BasicType.NULL; - case Marker.FALSE: - LOGGER.log(Level.FINE, "Read value: false"); - return Boolean.FALSE; - case Marker.TRUE: - LOGGER.log(Level.FINE, "Read value: true"); - return Boolean.TRUE; - case Marker.INTEGER: - LOGGER.log(Level.FINE, "Read value: integer"); - long ival = signExtend(readU29(), 29); - LOGGER.log(Level.FINER, "Integer value: {0}", ival); - return ival; - case Marker.DOUBLE: - LOGGER.log(Level.FINE, "Read value: double"); - double dval = readDouble(); - LOGGER.log(Level.FINER, "Double value: {0}", "" + dval); - return dval; - case Marker.STRING: - LOGGER.log(Level.FINE, "Read value: string"); - String sval = readUtf8Vr(stringTable); - LOGGER.log(Level.FINER, "String value: {0}", sval); - return sval; - case Marker.XML_DOC: - LOGGER.log(Level.FINE, "Read value: xml_doc"); - long xmlDocU29 = readU29(); - int xmlDocNoRefFlag = (int) (xmlDocU29 & 1); - if (xmlDocNoRefFlag == 1) { - long byteLength = xmlDocU29 >> 1; - String xval; - if (xmlDocU29 == 0) { - xval = ""; + try { + int marker = readU8("marker"); + markerswitch: + switch (marker) { + case Marker.UNDEFINED: + renameLastDump("undefined-marker"); + LOGGER.log(Level.FINE, "Read value: undefined"); + result = BasicType.UNDEFINED; + break; + case Marker.NULL: + renameLastDump("null-marker"); + LOGGER.log(Level.FINE, "Read value: null"); + result = BasicType.NULL; + break; + case Marker.FALSE: + renameLastDump("false-marker"); + LOGGER.log(Level.FINE, "Read value: false"); + result = Boolean.FALSE; + break; + case Marker.TRUE: + renameLastDump("true-marker"); + LOGGER.log(Level.FINE, "Read value: true"); + result = Boolean.TRUE; + break; + case Marker.INTEGER: + renameLastDump("integer-marker"); + LOGGER.log(Level.FINE, "Read value: integer"); + long ival = readS29("intValue"); + LOGGER.log(Level.FINER, "Integer value: {0}", ival); + result = ival; + break; + case Marker.DOUBLE: + renameLastDump("double-marker"); + LOGGER.log(Level.FINE, "Read value: double"); + double dval = readDouble("doubleValue"); + LOGGER.log(Level.FINER, "Double value: {0}", "" + dval); + result = dval; + break; + case Marker.STRING: + renameLastDump("string-marker"); + LOGGER.log(Level.FINE, "Read value: string"); + String sval = readUtf8Vr("stringValue", stringTable); + LOGGER.log(Level.FINER, "String value: {0}", sval); + result = sval; + break; + case Marker.XML_DOC: + renameLastDump("xml-doc-marker"); + LOGGER.log(Level.FINE, "Read value: xml_doc"); + long xmlDocU29 = readU29("U29"); + int xmlDocNoRefFlag = (int) (xmlDocU29 & 1); + if (xmlDocNoRefFlag == 1) { + renameLastDump("U29X-value"); + long byteLength = xmlDocU29 >> 1; + String xval = readUtf8Char("characters", byteLength); + LOGGER.log(Level.FINER, "XmlDoc value: {0}", xval); + XmlDocType retXmlDoc = new XmlDocType(xval); + objectTable.add(retXmlDoc); + result = retXmlDoc; } else { - byte buf[] = new byte[(int) byteLength]; //TODO: long strings, int is not enough for them - int cnt = is.read(buf); - if (cnt < buf.length) { - throw new PrematureEndOfTheStreamException(); - } - xval = new String(buf, "UTF-8"); + renameLastDump("U29O-ref"); + int refIndexXmlDoc = (int) (xmlDocU29 >> 1); + LOGGER.log(Level.FINER, "XmlDoc value: reference({0})", refIndexXmlDoc); + result = objectTable.get(refIndexXmlDoc); //What if it's not XmlRef? } - LOGGER.log(Level.FINER, "XmlDoc value: {0}", xval); - XmlDocType retXmlDoc = new XmlDocType(xval); - objectTable.add(retXmlDoc); - return retXmlDoc; - } else { - int refIndexXmlDoc = (int) (xmlDocU29 >> 1); - LOGGER.log(Level.FINER, "XmlDoc value: reference({0})", refIndexXmlDoc); - return objectTable.get(refIndexXmlDoc); //What if it's not XmlRef? - } - case Marker.DATE: - LOGGER.log(Level.FINE, "Read value: date"); - long dateU29 = readU29(); - int dateNoRefFlag = (int) (dateU29 & 1); - if (dateNoRefFlag == 1) { - //remaining bits of dateU29 are not used - double dtval = readDouble(); - DateType retDate = new DateType(dtval); - LOGGER.log(Level.FINER, "Date value: {0}", retDate); - objectTable.add(retDate); - return retDate; - } else { - int refIndexDate = (int) (dateU29 >> 1); - LOGGER.log(Level.FINER, "Date value: reference({0})", refIndexDate); - return objectTable.get(refIndexDate); //What if it's not Date? - } - case Marker.ARRAY: - LOGGER.log(Level.FINE, "Read value: array"); - long arrayU29 = readU29(); - int arrayNoRefFlag = (int) (arrayU29 & 1); - if (arrayNoRefFlag == 1) { - int denseCount = (int) (arrayU29 >> 1); - LOGGER.log(Level.FINEST, "Array value: denseCount={0}", new Object[]{denseCount}); - List> assocPart = new ArrayList<>(); - List densePart = new ArrayList<>(); - ArrayType retArray = new ArrayType(densePart, assocPart); - objectTable.add(retArray); //add before processing elements which may reference this + break; + case Marker.DATE: + renameLastDump("date-marker"); + LOGGER.log(Level.FINE, "Read value: date"); + long dateU29 = readU29("U29"); + int dateNoRefFlag = (int) (dateU29 & 1); + if (dateNoRefFlag == 1) { + renameLastDump("U29D-value"); + //remaining bits of dateU29 are not used + double dtval = readDouble("date-time"); + DateType retDate = new DateType(dtval); + LOGGER.log(Level.FINER, "Date value: {0}", retDate); + objectTable.add(retDate); + result = retDate; + } else { + renameLastDump("U29O-ref"); + int refIndexDate = (int) (dateU29 >> 1); + LOGGER.log(Level.FINER, "Date value: reference({0})", refIndexDate); + result = objectTable.get(refIndexDate); //What if it's not Date? + } + break; + case Marker.ARRAY: + renameLastDump("array-marker"); + LOGGER.log(Level.FINE, "Read value: array"); + long arrayU29 = readU29("U29"); + int arrayNoRefFlag = (int) (arrayU29 & 1); + if (arrayNoRefFlag == 1) { + renameLastDump("U29A-value"); + int denseCount = (int) (arrayU29 >> 1); + LOGGER.log(Level.FINEST, "Array value: denseCount={0}", new Object[]{denseCount}); + List> assocPart = new ArrayList<>(); + List densePart = new ArrayList<>(); + ArrayType retArray = new ArrayType(densePart, assocPart); + objectTable.add(retArray); //add before processing elements which may reference this - while (true) { - String key = readUtf8Vr(stringTable); - if (key.isEmpty()) { - break; - } else { + while (true) { + String key = readUtf8Vr("key", stringTable); + if (key.isEmpty()) { + renameLastDump("UTF-8-empty"); + break; + } else { + try { + Object val = readValue("value", serializers, objectTable, traitsTable, stringTable); + assocPart.add(new Pair<>(key, val)); + } catch (NoSerializerExistsException nse) { + assocPart.add(new Pair<>(key, nse.getIncompleteData())); + throw new NoSerializerExistsException(nse.getClassName(), retArray, nse); + } + } + } + LOGGER.log(Level.FINEST, "Array value: assocSize={0}", new Object[]{assocPart.size()}); + + for (int i = 0; i < denseCount; i++) { try { - Object val = readValue(serializers, objectTable, traitsTable, stringTable); - assocPart.add(new Pair<>(key, val)); + densePart.add(readValue("denseValue", serializers, objectTable, traitsTable, stringTable)); } catch (NoSerializerExistsException nse) { - assocPart.add(new Pair<>(key, nse.getIncompleteData())); + densePart.add(nse.getIncompleteData()); + for (int j = i + 1; j < denseCount; j++) { + densePart.add(BasicType.UNKNOWN); + } throw new NoSerializerExistsException(nse.getClassName(), retArray, nse); } } - } - LOGGER.log(Level.FINEST, "Array value: assocSize={0}", new Object[]{assocPart.size()}); + LOGGER.log(Level.FINER, "Array value: dense_size={0},assocSize={1}", new Object[]{densePart.size(), assocPart.size()}); + result = retArray; - for (int i = 0; i < denseCount; i++) { - try { - densePart.add(readValue(serializers, objectTable, traitsTable, stringTable)); - } catch (NoSerializerExistsException nse) { - densePart.add(nse.getIncompleteData()); - for (int j = i + 1; j < denseCount; j++) { - densePart.add(BasicType.UNKNOWN); - } - throw new NoSerializerExistsException(nse.getClassName(), retArray, nse); - } - } - LOGGER.log(Level.FINER, "Array value: dense_size={0},assocSize={1}", new Object[]{densePart.size(), assocPart.size()}); - return retArray; - - } else { - int refIndexArray = (int) (arrayU29 >> 1); - LOGGER.log(Level.FINER, "Array value: reference({0})", refIndexArray); - return objectTable.get(refIndexArray); //What if it's not Array? - } - - case Marker.OBJECT: - LOGGER.log(Level.FINE, "Read value: object"); - long objectU29 = readU29(); - int objectNoRefFlag = (int) (objectU29 & 1); - if (objectNoRefFlag == 1) { - Traits traits; - int objectTraitsNoRefFlag = (int) ((objectU29 >> 1) & 1); - if (objectTraitsNoRefFlag == 1) { - int objectTraitsExtFlag = (int) ((objectU29 >> 2) & 1); - ObjectType retObjectType; - if (objectTraitsExtFlag == 1) { - String className = readUtf8Vr(stringTable); - if (!serializers.containsKey(className)) { - throw new NoSerializerExistsException(className, new ObjectType(new Traits(className, false, new ArrayList<>()), (byte[]) null, new ArrayList<>()), null); - } - - MonitoredInputStream mis = new MonitoredInputStream(is); - List> serMembers = serializers.get(className).readObject(className, mis); - byte serData[] = mis.getReadData(); - Traits unserTraits = new Traits(className, false, new ArrayList<>()); - retObjectType = new ObjectType(unserTraits, serData, serMembers); - - LOGGER.log(Level.FINER, "Object/Traits value: customSerialized"); - objectTable.add(retObjectType); - return retObjectType; - } else { - int dynamicFlag = (int) ((objectU29 >> 3) & 1); - int numSealed = (int) (objectU29 >> 4); - LOGGER.log(Level.FINEST, "object dynamicFlag:{0}", dynamicFlag); - LOGGER.log(Level.FINEST, "object numSealed:{0}", numSealed); - String className = readUtf8Vr(stringTable); - LOGGER.log(Level.FINEST, "object className:{0}", className); - List sealedMemberNames = new ArrayList<>(); - for (int i = 0; i < numSealed; i++) { - sealedMemberNames.add(readUtf8Vr(stringTable)); - } - traits = new Traits(className, dynamicFlag == 1, sealedMemberNames); - } - traitsTable.add(traits); } else { - int refIndexTraits = (int) (objectU29 >> 2); - traits = traitsTable.get(refIndexTraits); - LOGGER.log(Level.FINER, "Traits value: reference({0}) - traitsize={1}", new Object[]{refIndexTraits, traits.getSealedMemberNames().size()}); + renameLastDump("U29O-ref"); + int refIndexArray = (int) (arrayU29 >> 1); + LOGGER.log(Level.FINER, "Array value: reference({0})", refIndexArray); + result = objectTable.get(refIndexArray); //What if it's not Array? } - List> sealedMembers = new ArrayList<>(); - List> dynamicMembers = new ArrayList<>(); + break; - Object retObjectType = new ObjectType(traits, sealedMembers, dynamicMembers); - objectTable.add(retObjectType); //add it before any subvalue can reference it - List sealedMemberValues = new ArrayList<>(); - NoSerializerExistsException error = null; + case Marker.OBJECT: + renameLastDump("object-marker"); + LOGGER.log(Level.FINE, "Read value: object"); + long objectU29 = readU29("U29"); + int objectNoRefFlag = (int) (objectU29 & 1); + if (objectNoRefFlag == 1) { + Traits traits; + int objectTraitsNoRefFlag = (int) ((objectU29 >> 1) & 1); + if (objectTraitsNoRefFlag == 1) { + int objectTraitsExtFlag = (int) ((objectU29 >> 2) & 1); + ObjectType retObjectType; + if (objectTraitsExtFlag == 1) { + renameLastDump("U29O-traits-ext"); + String className = readUtf8Vr("className", stringTable); + if (!serializers.containsKey(className)) { + throw new NoSerializerExistsException(className, new ObjectType(new Traits(className, false, new ArrayList<>()), (byte[]) null, new ArrayList<>()), null); + } - for (int i = 0; i < traits.getSealedMemberNames().size(); i++) { - try { - sealedMemberValues.add(readValue(serializers, objectTable, traitsTable, stringTable)); - } catch (NoSerializerExistsException nse) { - sealedMemberValues.add(nse.getIncompleteData()); - for (int j = i + 1; j < traits.getSealedMemberNames().size(); j++) { - sealedMemberValues.add(BasicType.UNKNOWN); + MonitoredInputStream mis = new MonitoredInputStream(is); + List> serMembers = serializers.get(className).readObject(className, mis); + byte serData[] = mis.getReadData(); + Traits unserTraits = new Traits(className, false, new ArrayList<>()); + retObjectType = new ObjectType(unserTraits, serData, serMembers); + + LOGGER.log(Level.FINER, "Object/Traits value: customSerialized"); + objectTable.add(retObjectType); + result = retObjectType; + break markerswitch; + } else { + renameLastDump("U29O-traits"); + int dynamicFlag = (int) ((objectU29 >> 3) & 1); + int numSealed = (int) (objectU29 >> 4); + LOGGER.log(Level.FINEST, "object dynamicFlag:{0}", dynamicFlag); + LOGGER.log(Level.FINEST, "object numSealed:{0}", numSealed); + String className = readUtf8Vr("className", stringTable); + LOGGER.log(Level.FINEST, "object className:{0}", className); + List sealedMemberNames = new ArrayList<>(); + for (int i = 0; i < numSealed; i++) { + sealedMemberNames.add(readUtf8Vr("sealedMemberName", stringTable)); + } + traits = new Traits(className, dynamicFlag == 1, sealedMemberNames); + traitsTable.add(traits); } - error = nse; - break; + } else { + renameLastDump("U29O-traits-ref"); + int refIndexTraits = (int) (objectU29 >> 2); + traits = traitsTable.get(refIndexTraits); + LOGGER.log(Level.FINER, "Traits value: reference({0}) - traitsize={1}", new Object[]{refIndexTraits, traits.getSealedMemberNames().size()}); } - } + List> sealedMembers = new ArrayList<>(); + List> dynamicMembers = new ArrayList<>(); - for (int i = 0; i < traits.getSealedMemberNames().size(); i++) { - sealedMembers.add(new Pair<>(traits.getSealedMemberNames().get(i), sealedMemberValues.get(i))); - } - if (traits.isDynamic()) { - String dynamicMemberName; - while (!(dynamicMemberName = readUtf8Vr(stringTable)).isEmpty()) { + Object retObjectType = new ObjectType(traits, sealedMembers, dynamicMembers); + objectTable.add(retObjectType); //add it before any subvalue can reference it + List sealedMemberValues = new ArrayList<>(); + NoSerializerExistsException error = null; + + for (int i = 0; i < traits.getSealedMemberNames().size(); i++) { try { - Object dynamicMemberValue = readValue(serializers, objectTable, traitsTable, stringTable); - dynamicMembers.add(new Pair<>(dynamicMemberName, dynamicMemberValue)); + sealedMemberValues.add(readValue("sealedMemberValue", serializers, objectTable, traitsTable, stringTable)); } catch (NoSerializerExistsException nse) { - dynamicMembers.add(new Pair<>(dynamicMemberName, nse.getIncompleteData())); - throw new NoSerializerExistsException(nse.getClassName(), retObjectType, nse); + sealedMemberValues.add(nse.getIncompleteData()); + for (int j = i + 1; j < traits.getSealedMemberNames().size(); j++) { + sealedMemberValues.add(BasicType.UNKNOWN); + } + error = nse; + break; } } - } - LOGGER.log(Level.FINER, "Object value: dynamic={0},className={1},sealedSize={2},dynamicSize={3}", new Object[]{traits.isDynamic(), traits.getClassName(), sealedMembers.size(), dynamicMembers.size()}); - return retObjectType; - } else { - int refIndexObject = (int) (objectU29 >> 1); - LOGGER.log(Level.FINER, "Object value: reference({0})", refIndexObject); - return objectTable.get(refIndexObject); - } - case Marker.XML: - LOGGER.log(Level.FINE, "Read value: xml"); - long xmlU29 = readU29(); - int xmlNoRefFlag = (int) (xmlU29 & 1); - if (xmlNoRefFlag == 1) { - int byteLength = (int) (xmlU29 >> 1); //TODO: long strings, int is not enough for them - String xString = readUtf8Char(byteLength); - XmlType retXmlType = new XmlType(xString); - LOGGER.log(Level.FINER, "Xml value: {0}", xString); - objectTable.add(retXmlType); - return retXmlType; - } else { - int refIndexXml = (int) (xmlU29 >> 1); - LOGGER.log(Level.FINER, "XML value: reference({0})", refIndexXml); - return objectTable.get(refIndexXml); - } - case Marker.BYTE_ARRAY: - LOGGER.log(Level.FINE, "Read value: bytearray"); - long byteArrayU29 = readU29(); - int byteArrayNoRefFlag = (int) (byteArrayU29 & 1); - if (byteArrayNoRefFlag == 1) { - int byteArrayLength = (int) (byteArrayU29 >> 1); - byte byteArrayBuf[] = new byte[byteArrayLength]; - if (is.read(byteArrayBuf) != byteArrayLength) { - throw new PrematureEndOfTheStreamException(); - } - - LOGGER.log(Level.FINER, "ByteArray value: bytes[{0}]", byteArrayLength); - ByteArrayType retByteArrayType = new ByteArrayType(byteArrayBuf); - objectTable.add(retByteArrayType); - return retByteArrayType; - } else { - int refIndexByteArray = (int) (byteArrayU29 >> 1); - LOGGER.log(Level.FINER, "ByteArray value: reference({0})", refIndexByteArray); - return objectTable.get(refIndexByteArray); - } - case Marker.VECTOR_INT: - LOGGER.log(Level.FINE, "Read value: vector_int"); - long vectorIntU29 = readU29(); - int vectorIntNoRefFlag = (int) (vectorIntU29 & 1); - if (vectorIntNoRefFlag == 1) { - int vectorIntCountItems = (int) (vectorIntU29 >> 1); - int fixed = readU8(); - List vals = new ArrayList<>(); - for (int i = 0; i < vectorIntCountItems; i++) { - vals.add(readU32()); - } - VectorIntType retVectorInt = new VectorIntType(fixed == 1, vals); - LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}]", new Object[]{fixed, vectorIntCountItems}); - objectTable.add(retVectorInt); - return retVectorInt; - } else { - int refIndexVectorInt = (int) (vectorIntU29 >> 1); - LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorInt); - return objectTable.get(refIndexVectorInt); - } - case Marker.VECTOR_UINT: - LOGGER.log(Level.FINE, "Read value: vector_uint"); - long vectorUIntU29 = readU29(); - int vectorUIntNoRefFlag = (int) (vectorUIntU29 & 1); - if (vectorUIntNoRefFlag == 1) { - int vectorUIntCountItems = (int) (vectorUIntU29 >> 1); - int fixed = readU8(); - List vals = new ArrayList<>(); - for (int i = 0; i < vectorUIntCountItems; i++) { - vals.add(signExtend(readU32(), 32)); - } - VectorUIntType retVectorUInt = new VectorUIntType(fixed == 1, vals); - LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}]", new Object[]{fixed, vectorUIntCountItems}); - objectTable.add(retVectorUInt); - return retVectorUInt; - } else { - int refIndexVectorUInt = (int) (vectorUIntU29 >> 1); - LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorUInt); - return objectTable.get(refIndexVectorUInt); - } - case Marker.VECTOR_DOUBLE: - LOGGER.log(Level.FINE, "Read value: vector_double"); - long vectorDoubleU29 = readU29(); - int vectorDoubleNoRefFlag = (int) (vectorDoubleU29 & 1); - if (vectorDoubleNoRefFlag == 1) { - int vectorDoubleCountItems = (int) (vectorDoubleU29 >> 1); - int fixed = readU8(); - List vals = new ArrayList<>(); - for (int i = 0; i < vectorDoubleCountItems; i++) { - vals.add(readDouble()); - } - VectorDoubleType retVectorDouble = new VectorDoubleType(fixed == 1, vals); - LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}]", new Object[]{fixed, vectorDoubleCountItems}); - objectTable.add(retVectorDouble); - return retVectorDouble; - } else { - int refIndexVectorDouble = (int) (vectorDoubleU29 >> 1); - LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorDouble); - return objectTable.get(refIndexVectorDouble); - } - case Marker.VECTOR_OBJECT: - LOGGER.log(Level.FINE, "Read value: vector_object"); - long vectorObjectU29 = readU29(); - int vectorObjectNoRefFlag = (int) (vectorObjectU29 & 1); - if (vectorObjectNoRefFlag == 1) { - int vectorObjectCountItems = (int) (vectorObjectU29 >> 1); - int fixed = readU8(); - String objectTypeName = readUtf8Vr(stringTable); //uses "*" for any type - List vals = new ArrayList<>(); - NoSerializerExistsException error = null; - for (int i = 0; i < vectorObjectCountItems; i++) { - try { - vals.add(readValue(serializers, objectTable, traitsTable, stringTable)); - } catch (NoSerializerExistsException nse) { - vals.add(nse.getIncompleteData()); - for (int j = i + 1; j < vectorObjectCountItems; j++) { - vals.add(BasicType.UNKNOWN); - } - error = nse; - break; + for (int i = 0; i < traits.getSealedMemberNames().size(); i++) { + sealedMembers.add(new Pair<>(traits.getSealedMemberNames().get(i), sealedMemberValues.get(i))); } + if (traits.isDynamic()) { + String dynamicMemberName; + while (!(dynamicMemberName = readUtf8Vr("dynamicMemberName", stringTable)).isEmpty()) { + try { + Object dynamicMemberValue = readValue("dynamicMemberValue", serializers, objectTable, traitsTable, stringTable); + dynamicMembers.add(new Pair<>(dynamicMemberName, dynamicMemberValue)); + } catch (NoSerializerExistsException nse) { + dynamicMembers.add(new Pair<>(dynamicMemberName, nse.getIncompleteData())); + throw new NoSerializerExistsException(nse.getClassName(), retObjectType, nse); + } + } + renameLastDump("UTF-8-empty"); + } + + LOGGER.log(Level.FINER, "Object value: dynamic={0},className={1},sealedSize={2},dynamicSize={3}", new Object[]{traits.isDynamic(), traits.getClassName(), sealedMembers.size(), dynamicMembers.size()}); + result = retObjectType; + } else { + renameLastDump("U29O-ref"); + int refIndexObject = (int) (objectU29 >> 1); + LOGGER.log(Level.FINER, "Object value: reference({0})", refIndexObject); + result = objectTable.get(refIndexObject); } - VectorObjectType retVectorObject = new VectorObjectType(fixed == 1, objectTypeName, vals); - LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}, typeName:{2}]", new Object[]{fixed, vectorObjectCountItems, objectTypeName}); - objectTable.add(retVectorObject); - if (error != null) { - throw new NoSerializerExistsException(error.getClassName(), retVectorObject, error); + break; + case Marker.XML: + renameLastDump("xml-marker"); + LOGGER.log(Level.FINE, "Read value: xml"); + long xmlU29 = readU29("U29"); + int xmlNoRefFlag = (int) (xmlU29 & 1); + if (xmlNoRefFlag == 1) { + renameLastDump("U29X-value"); + long byteLength = (xmlU29 >> 1); + String xString = readUtf8Char("characters", byteLength); + XmlType retXmlType = new XmlType(xString); + LOGGER.log(Level.FINER, "Xml value: {0}", xString); + objectTable.add(retXmlType); + result = retXmlType; + } else { + renameLastDump("U29O-ref"); + int refIndexXml = (int) (xmlU29 >> 1); + LOGGER.log(Level.FINER, "XML value: reference({0})", refIndexXml); + result = objectTable.get(refIndexXml); } - return retVectorObject; - } else { - int refIndexVectorObject = (int) (vectorObjectU29 >> 1); - LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorObject); - return objectTable.get(refIndexVectorObject); - } - case Marker.DICTIONARY: - long dictionaryObjectU29 = readU29(); - int dictionaryNoRefFlag = (int) (dictionaryObjectU29 & 1); - if (dictionaryNoRefFlag == 1) { - int numEntries = (int) (dictionaryObjectU29 >> 1); - int weakKeys = readU8(); - List> data = new ArrayList<>(); - DictionaryType retDictionary = new DictionaryType(weakKeys == 1, data); - objectTable.add(retDictionary); - NoSerializerExistsException error = null; - for (int i = 0; i < numEntries; i++) { - Object key; - Object val; - try { - key = readValue(serializers, objectTable, traitsTable, stringTable); + break; + case Marker.BYTE_ARRAY: + renameLastDump("byte-array-marker"); + LOGGER.log(Level.FINE, "Read value: bytearray"); + long byteArrayU29 = readU29("U29"); + int byteArrayNoRefFlag = (int) (byteArrayU29 & 1); + if (byteArrayNoRefFlag == 1) { + renameLastDump("U29B-value"); + int byteArrayLength = (int) (byteArrayU29 >> 1); + byte byteArrayBuf[] = new byte[byteArrayLength]; + if (is.read(byteArrayBuf) != byteArrayLength) { + throw new PrematureEndOfTheStreamException(); + } + + LOGGER.log(Level.FINER, "ByteArray value: bytes[{0}]", byteArrayLength); + ByteArrayType retByteArrayType = new ByteArrayType(byteArrayBuf); + objectTable.add(retByteArrayType); + result = retByteArrayType; + } else { + renameLastDump("U290-ref"); + int refIndexByteArray = (int) (byteArrayU29 >> 1); + LOGGER.log(Level.FINER, "ByteArray value: reference({0})", refIndexByteArray); + result = objectTable.get(refIndexByteArray); + } + break; + case Marker.VECTOR_INT: + renameLastDump("vector-int-marker"); + LOGGER.log(Level.FINE, "Read value: vector_int"); + long vectorIntU29 = readU29("U29"); + int vectorIntNoRefFlag = (int) (vectorIntU29 & 1); + if (vectorIntNoRefFlag == 1) { + renameLastDump("U29V-value"); + int vectorIntCountItems = (int) (vectorIntU29 >> 1); + int fixed = readU8("fixed"); + List vals = new ArrayList<>(); + for (int i = 0; i < vectorIntCountItems; i++) { + vals.add(readS32("value")); + } + VectorIntType retVectorInt = new VectorIntType(fixed == 1, vals); + LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}]", new Object[]{fixed, vectorIntCountItems}); + objectTable.add(retVectorInt); + result = retVectorInt; + } else { + renameLastDump("U29O-ref"); + int refIndexVectorInt = (int) (vectorIntU29 >> 1); + LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorInt); + result = objectTable.get(refIndexVectorInt); + } + break; + case Marker.VECTOR_UINT: + renameLastDump("vector-uint-marker"); + LOGGER.log(Level.FINE, "Read value: vector_uint"); + long vectorUIntU29 = readU29("U29"); + int vectorUIntNoRefFlag = (int) (vectorUIntU29 & 1); + if (vectorUIntNoRefFlag == 1) { + renameLastDump("U29V-value"); + int vectorUIntCountItems = (int) (vectorUIntU29 >> 1); + int fixed = readU8("fixed"); + List vals = new ArrayList<>(); + for (int i = 0; i < vectorUIntCountItems; i++) { + vals.add(readU32("value")); + } + VectorUIntType retVectorUInt = new VectorUIntType(fixed == 1, vals); + LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}]", new Object[]{fixed, vectorUIntCountItems}); + objectTable.add(retVectorUInt); + result = retVectorUInt; + } else { + renameLastDump("U29O-ref"); + int refIndexVectorUInt = (int) (vectorUIntU29 >> 1); + LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorUInt); + result = objectTable.get(refIndexVectorUInt); + } + break; + case Marker.VECTOR_DOUBLE: + renameLastDump("vector-double-marker"); + LOGGER.log(Level.FINE, "Read value: vector_double"); + long vectorDoubleU29 = readU29("U29"); + int vectorDoubleNoRefFlag = (int) (vectorDoubleU29 & 1); + if (vectorDoubleNoRefFlag == 1) { + renameLastDump("U29V-value"); + int vectorDoubleCountItems = (int) (vectorDoubleU29 >> 1); + int fixed = readU8("fixed"); + List vals = new ArrayList<>(); + for (int i = 0; i < vectorDoubleCountItems; i++) { + vals.add(readDouble("value")); + } + VectorDoubleType retVectorDouble = new VectorDoubleType(fixed == 1, vals); + LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}]", new Object[]{fixed, vectorDoubleCountItems}); + objectTable.add(retVectorDouble); + result = retVectorDouble; + } else { + renameLastDump("U29O-ref"); + int refIndexVectorDouble = (int) (vectorDoubleU29 >> 1); + LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorDouble); + result = objectTable.get(refIndexVectorDouble); + } + break; + case Marker.VECTOR_OBJECT: + renameLastDump("vector-object-marker"); + LOGGER.log(Level.FINE, "Read value: vector_object"); + long vectorObjectU29 = readU29("U29"); + int vectorObjectNoRefFlag = (int) (vectorObjectU29 & 1); + if (vectorObjectNoRefFlag == 1) { + renameLastDump("U29V-value"); + int vectorObjectCountItems = (int) (vectorObjectU29 >> 1); + int fixed = readU8("fixed"); + String objectTypeName = readUtf8Vr("object-type-name", stringTable); //uses "*" for any type + List vals = new ArrayList<>(); + NoSerializerExistsException error = null; + for (int i = 0; i < vectorObjectCountItems; i++) { try { - val = readValue(serializers, objectTable, traitsTable, stringTable); + vals.add(readValue("value", serializers, objectTable, traitsTable, stringTable)); + } catch (NoSerializerExistsException nse) { + vals.add(nse.getIncompleteData()); + for (int j = i + 1; j < vectorObjectCountItems; j++) { + vals.add(BasicType.UNKNOWN); + } + error = nse; + break; + } + } + VectorObjectType retVectorObject = new VectorObjectType(fixed == 1, objectTypeName, vals); + LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}, typeName:{2}]", new Object[]{fixed, vectorObjectCountItems, objectTypeName}); + objectTable.add(retVectorObject); + if (error != null) { + throw new NoSerializerExistsException(error.getClassName(), retVectorObject, error); + } + result = retVectorObject; + } else { + renameLastDump("U29O-ref"); + + int refIndexVectorObject = (int) (vectorObjectU29 >> 1); + LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorObject); + result = objectTable.get(refIndexVectorObject); + } + break; + case Marker.DICTIONARY: + renameLastDump("dictionary-marker"); + long dictionaryObjectU29 = readU29("U29"); + int dictionaryNoRefFlag = (int) (dictionaryObjectU29 & 1); + if (dictionaryNoRefFlag == 1) { + renameLastDump("U29Dict-value"); + int numEntries = (int) (dictionaryObjectU29 >> 1); + int weakKeys = readU8("fixed"); + List> data = new ArrayList<>(); + DictionaryType retDictionary = new DictionaryType(weakKeys == 1, data); + objectTable.add(retDictionary); + NoSerializerExistsException error = null; + for (int i = 0; i < numEntries; i++) { + Object key; + Object val; + try { + key = readValue("entry-key", serializers, objectTable, traitsTable, stringTable); + try { + val = readValue("entry-value", serializers, objectTable, traitsTable, stringTable); + } catch (NoSerializerExistsException nse) { + error = nse; + val = BasicType.UNKNOWN; + } } catch (NoSerializerExistsException nse) { error = nse; + key = BasicType.UNKNOWN; val = BasicType.UNKNOWN; } - } catch (NoSerializerExistsException nse) { - error = nse; - key = BasicType.UNKNOWN; - val = BasicType.UNKNOWN; - } - data.add(new Pair<>(key, val)); - if (error != null) { - for (int j = i + 1; j < numEntries; j++) { - data.add(new Pair<>(BasicType.UNKNOWN, BasicType.UNKNOWN)); + data.add(new Pair<>(key, val)); + if (error != null) { + for (int j = i + 1; j < numEntries; j++) { + data.add(new Pair<>(BasicType.UNKNOWN, BasicType.UNKNOWN)); + } + break; } - break; } + if (error != null) { + throw new NoSerializerExistsException(error.getClassName(), retDictionary, error); + } + result = retDictionary; + } else { + renameLastDump("U29O-ref"); + int refIndexDictionary = (int) (dictionaryObjectU29 >> 1); + LOGGER.log(Level.FINER, "Dictionary value: reference({0})", refIndexDictionary); + result = objectTable.get(refIndexDictionary); } - if (error != null) { - throw new NoSerializerExistsException(error.getClassName(), retDictionary, error); - } - return retDictionary; - } else { - int refIndexDictionary = (int) (dictionaryObjectU29 >> 1); - LOGGER.log(Level.FINER, "Dictionary value: reference({0})", refIndexDictionary); - return objectTable.get(refIndexDictionary); - } - default: - throw new UnsupportedValueTypeException(marker); + break; + default: + throw new UnsupportedValueTypeException(marker); + } + } finally { + endDumpLevel(); } + return result; } private class MonitoredInputStream extends InputStream { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java index cf8eada0e..f078ef36b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.EndOfStreamException; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.amf.amf3.NoSerializerExistsException; import com.jpexs.decompiler.flash.tags.base.ASMSourceContainer; import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; import com.jpexs.decompiler.flash.types.BasicType; @@ -37,10 +38,13 @@ import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.decompiler.flash.types.filters.FILTER; import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.Helper; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Same as PlaceObject3Tag except additional AMF data @@ -230,7 +234,8 @@ public class PlaceObject4Tag extends PlaceObjectTypeTag implements ASMSourceCont @Reserved public boolean reserved; - public byte[] amfData; //TODO: Parse AMF data? + //public byte[] amfData; //TODO: Parse AMF data? + public Object amfData; /** * Constructor @@ -241,7 +246,7 @@ public class PlaceObject4Tag extends PlaceObjectTypeTag implements ASMSourceCont super(swf, ID, NAME, null); } - public PlaceObject4Tag(SWF swf, boolean placeFlagMove, int depth, String className, int characterId, MATRIX matrix, CXFORMWITHALPHA colorTransform, int ratio, String name, int clipDepth, List surfaceFilterList, int blendMode, int bitmapCache, int visible, RGBA backgroundColor, CLIPACTIONS clipActions, byte[] amfData) { + public PlaceObject4Tag(SWF swf, boolean placeFlagMove, int depth, String className, int characterId, MATRIX matrix, CXFORMWITHALPHA colorTransform, int ratio, String name, int clipDepth, List surfaceFilterList, int blendMode, int bitmapCache, int visible, RGBA backgroundColor, CLIPACTIONS clipActions, Object amfData) { super(swf, ID, NAME, null); this.placeFlagHasClassName = className != null; this.placeFlagHasFilterList = surfaceFilterList != null; @@ -353,8 +358,9 @@ public class PlaceObject4Tag extends PlaceObjectTypeTag implements ASMSourceCont if (placeFlagHasClipActions) { clipActions = sis.readCLIPACTIONS(swf, this, "clipActions"); } - - amfData = sis.readBytesEx(sis.available(), "amfData"); + if (sis.available() > 0) { + amfData = sis.readAmf3Object("amfValue"); + } } /** @@ -424,6 +430,14 @@ public class PlaceObject4Tag extends PlaceObjectTypeTag implements ASMSourceCont if (placeFlagHasClipActions) { sos.writeCLIPACTIONS(clipActions); } + if (amfData != null) { + //sos.write(Helper.readFile("d:\\Dropbox\\Programovani\\JavaSE\\FFDec\\libsrc\\ffdec_lib\\testdata\\amf3\\generated\\all.bin")); + try { + sos.writeAmf3Object(amfData); + } catch (NoSerializerExistsException ex) { + throw new IOException("Class \"" + ex.getClassName() + "\" implements IExternalizable, it cannot be saved"); + } + } } @Override diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java index 1f5104119..dd4bab2e9 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStreamTest.java @@ -4,7 +4,10 @@ import com.jpexs.decompiler.flash.amf.amf3.types.ArrayType; import com.jpexs.decompiler.flash.amf.amf3.types.DictionaryType; import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; import com.jpexs.decompiler.flash.amf.amf3.types.VectorObjectType; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.MemoryInputStream; import java.io.ByteArrayInputStream; +import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; @@ -20,7 +23,6 @@ import org.testng.annotations.Test; public class Amf3InputStreamTest { - private FileInputStream fis; private Amf3InputStream is; @AfterTest @@ -33,25 +35,17 @@ public class Amf3InputStreamTest { } is = null; } - if (fis != null) { - try { - fis.close(); - } catch (IOException ex) { - //ignore - } - fis = null; - } } - private void initStream(String fileName) throws FileNotFoundException { - fis = new FileInputStream("testdata/amf3/generated/" + fileName); - is = new Amf3InputStream(fis); + private void initStream(String fileName) throws IOException { + String file = "testdata/amf3/generated/" + fileName; + is = new Amf3InputStream(new MemoryInputStream(Helper.readFile(file))); } @Test public void testReadObject() throws IOException, NoSerializerExistsException { initStream("all.bin"); - is.readValue(); + is.readValue("testValue"); } private Map getSerializers() { @@ -76,20 +70,20 @@ public class Amf3InputStreamTest { @Test(expectedExceptions = NoSerializerExistsException.class) public void testReadCustomSerializedNeedsSerializer() throws IOException, NoSerializerExistsException { initStream("custom.bin"); - is.readValue(); //needs deserializer for CustomClass + is.readValue("testValue"); //needs deserializer for CustomClass } @Test public void testReadCustomSerialized() throws IOException, NoSerializerExistsException { initStream("custom.bin"); - is.readValue(getSerializers()); + is.readValue("testValue", getSerializers()); } @Test public void testNoSerializerHandlingInObjectDynamicProp() throws IOException { initStream("noserializer_object_dynamic.bin"); try { - is.readValue(); + is.readValue("testValue"); } catch (NoSerializerExistsException ex) { assertTrue(ex.getIncompleteData() instanceof ObjectType, "Expected datatype: ObjectType, Actual datatype: " + ex.getIncompleteData().getClass()); //TODO: examinate the data more @@ -100,7 +94,7 @@ public class Amf3InputStreamTest { public void testNoSerializerHandlingInObjectSealedProp() throws IOException { initStream("noserializer_object_sealed.bin"); try { - is.readValue(); + is.readValue("testValue"); } catch (NoSerializerExistsException ex) { assertTrue(ex.getIncompleteData() instanceof ObjectType, "Expected datatype: ObjectType, Actual datatype: " + ex.getIncompleteData().getClass()); //TODO: examinate the data more @@ -111,7 +105,7 @@ public class Amf3InputStreamTest { public void testNoSerializerHandlingInArrayDense() throws IOException { initStream("noserializer_array_dense.bin"); try { - is.readValue(); + is.readValue("testValue"); } catch (NoSerializerExistsException ex) { assertTrue(ex.getIncompleteData() instanceof ArrayType, "Expected datatype: ArrayType, Actual datatype: " + ex.getIncompleteData().getClass()); //TODO: examinate the data more @@ -122,7 +116,7 @@ public class Amf3InputStreamTest { public void testNoSerializerHandlingInArrayAssociative() throws IOException { initStream("noserializer_array_associative.bin"); try { - is.readValue(); + is.readValue("testValue"); } catch (NoSerializerExistsException ex) { assertTrue(ex.getIncompleteData() instanceof ArrayType, "Expected datatype: ArrayType, Actual datatype: " + ex.getIncompleteData().getClass()); //TODO: examinate the data more @@ -133,7 +127,7 @@ public class Amf3InputStreamTest { public void testNoSerializerHandlingInVector() throws IOException { initStream("noserializer_vector.bin"); try { - is.readValue(); + is.readValue("testValue"); } catch (NoSerializerExistsException ex) { assertTrue(ex.getIncompleteData() instanceof VectorObjectType, "Expected datatype: VectorObjectType, Actual datatype: " + ex.getIncompleteData().getClass()); //TODO: examinate the data more @@ -144,7 +138,7 @@ public class Amf3InputStreamTest { public void testNoSerializerHandlingInDictionaryValues() throws IOException { initStream("noserializer_dictionary_value.bin"); try { - is.readValue(); + is.readValue("testValue"); } catch (NoSerializerExistsException ex) { assertTrue(ex.getIncompleteData() instanceof DictionaryType, "Expected datatype: DictionaryType, Actual datatype: " + ex.getIncompleteData().getClass()); //TODO: examinate the data more @@ -155,7 +149,7 @@ public class Amf3InputStreamTest { public void testNoSerializerHandlingInDictionaryKeys() throws IOException { initStream("noserializer_dictionary_key.bin"); try { - is.readValue(); + is.readValue("testValue"); } catch (NoSerializerExistsException ex) { assertTrue(ex.getIncompleteData() instanceof DictionaryType, "Expected datatype: DictionaryType, Actual datatype: " + ex.getIncompleteData().getClass()); //TODO: examinate the data more @@ -165,7 +159,7 @@ public class Amf3InputStreamTest { @Test(expectedExceptions = UnsupportedValueTypeException.class) public void testUnsupportedMarker() throws IOException, NoSerializerExistsException { final int UNSUPPORTED_MARKER = 100; - is = new Amf3InputStream(new ByteArrayInputStream(new byte[]{UNSUPPORTED_MARKER})); - is.readValue(); + is = new Amf3InputStream(new MemoryInputStream(new byte[]{UNSUPPORTED_MARKER})); + is.readValue("testValue"); } } diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java index 23f2fd0fc..af5abf607 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java @@ -1,5 +1,7 @@ package com.jpexs.decompiler.flash.amf.amf3; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.MemoryInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; @@ -76,23 +78,23 @@ public class Amf3OutputStreamTest { @Test(dataProvider = "files") public void testRecompile(String fileName) throws FileNotFoundException, IOException, NoSerializerExistsException { - File originalFile = new File("testdata/amf3/generated/" + fileName); + String originalFile = "testdata/amf3/generated/" + fileName; - byte[] originalData = readFile(originalFile); + byte[] originalData = Helper.readFile(originalFile); byte[] savedData; - try (FileInputStream fis = new FileInputStream(originalFile)) { - Amf3InputStream is = new Amf3InputStream(fis); - Object val = is.readValue(getSerializers()); - File savedFile = new File("testdata/amf3/generated/recompiled." + fileName); - try (FileOutputStream fos = new FileOutputStream(savedFile)) { - Amf3OutputStream os = new Amf3OutputStream(fos); - os.writeValue(val, getSerializers()); - } - savedData = readFile(savedFile); - savedFile.delete(); + Amf3InputStream is = new Amf3InputStream(new MemoryInputStream(Helper.readFile(originalFile))); + + Object val = is.readValue("testValue", getSerializers()); + String savedFile = "testdata/amf3/generated/recompiled." + fileName; + try (FileOutputStream fos = new FileOutputStream(savedFile)) { + Amf3OutputStream os = new Amf3OutputStream(fos); + os.writeValue(val, getSerializers()); } + savedData = Helper.readFile(savedFile); Assert.assertEquals(savedData, originalData); + new File(savedFile).delete(); + } } From 38eb716fdd6c3ac4d0a0bee2de886079cc45b45a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Mon, 18 Jul 2016 09:05:41 +0200 Subject: [PATCH 11/20] case fix 1 --- .../amf/amf3/PrematureEndOfTheStreamException_.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException_.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException_.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException_.java new file mode 100644 index 000000000..8c85fa2df --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException_.java @@ -0,0 +1,11 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +import java.io.IOException; + +public class PrematureEndOfTheStreamException extends IOException { + + public PrematureEndOfTheStreamException() { + super("Premature end of the stream reached"); + } + +} From 4714cd432efc01985f989d4fbc7e2827e36c5e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Mon, 18 Jul 2016 09:06:24 +0200 Subject: [PATCH 12/20] case fix 2 --- .../amf/amf3/PrematureEndOfTheStreamException.java | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException.java deleted file mode 100644 index 8c85fa2df..000000000 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.jpexs.decompiler.flash.amf.amf3; - -import java.io.IOException; - -public class PrematureEndOfTheStreamException extends IOException { - - public PrematureEndOfTheStreamException() { - super("Premature end of the stream reached"); - } - -} From fdb45d4f2c751174e534e7f04df28a87be86f416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Mon, 18 Jul 2016 09:06:42 +0200 Subject: [PATCH 13/20] case fix 3 --- ...treamException_.java => PrematureEndOfTheStreamException.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/{PrematureEndOfTheStreamException_.java => PrematureEndOfTheStreamException.java} (100%) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException_.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException.java similarity index 100% rename from libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException_.java rename to libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException.java From ce910766938debd10d148935faa9dd4301ebb2ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Mon, 18 Jul 2016 09:11:09 +0200 Subject: [PATCH 14/20] Amf3InputStream uses EndOfStreamException as SWF --- .../decompiler/flash/amf/amf3/Amf3InputStream.java | 7 ++++--- .../amf/amf3/PrematureEndOfTheStreamException.java | 11 ----------- 2 files changed, 4 insertions(+), 14 deletions(-) delete mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java index 2134f9584..7165cb14e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java @@ -1,5 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3; +import com.jpexs.decompiler.flash.EndOfStreamException; import com.jpexs.decompiler.flash.amf.amf3.types.ArrayType; import com.jpexs.decompiler.flash.amf.amf3.types.XmlType; import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; @@ -187,7 +188,7 @@ public class Amf3InputStream extends InputStream { byte buf[] = new byte[(int) byteLength]; //how about long strings(?), will the int length be enough? int cnt = is.read(buf); if (cnt < buf.length) { - throw new PrematureEndOfTheStreamException(); + throw new EndOfStreamException(); } String retString = new String(buf, "UTF-8"); endDumpLevel(retString); @@ -220,7 +221,7 @@ public class Amf3InputStream extends InputStream { private int readInternal() throws IOException { int ret = read(); if (ret == -1) { - throw new PrematureEndOfTheStreamException(); + throw new EndOfStreamException(); } return ret; } @@ -510,7 +511,7 @@ public class Amf3InputStream extends InputStream { int byteArrayLength = (int) (byteArrayU29 >> 1); byte byteArrayBuf[] = new byte[byteArrayLength]; if (is.read(byteArrayBuf) != byteArrayLength) { - throw new PrematureEndOfTheStreamException(); + throw new EndOfStreamException(); } LOGGER.log(Level.FINER, "ByteArray value: bytes[{0}]", byteArrayLength); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException.java deleted file mode 100644 index 8c85fa2df..000000000 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/PrematureEndOfTheStreamException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.jpexs.decompiler.flash.amf.amf3; - -import java.io.IOException; - -public class PrematureEndOfTheStreamException extends IOException { - - public PrematureEndOfTheStreamException() { - super("Premature end of the stream reached"); - } - -} From d82f0f17c3ea2060e39bdec584fb99411901068f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Mon, 18 Jul 2016 21:32:08 +0200 Subject: [PATCH 15/20] Improved dumpInfo in AMF3 --- .../flash/amf/amf3/Amf3InputStream.java | 273 ++++++++++++++---- 1 file changed, 211 insertions(+), 62 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java index 7165cb14e..c06785560 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java @@ -14,11 +14,15 @@ import com.jpexs.decompiler.flash.amf.amf3.types.DictionaryType; import com.jpexs.decompiler.flash.amf.amf3.types.VectorUIntType; import com.jpexs.decompiler.flash.amf.amf3.types.BasicType; import com.jpexs.decompiler.flash.dumpview.DumpInfo; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.helpers.Helper; import com.jpexs.helpers.MemoryInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -30,6 +34,11 @@ public class Amf3InputStream extends InputStream { public final static Logger LOGGER = Logger.getLogger(Amf3InputStream.class.getName()); private final MemoryInputStream is; public DumpInfo dumpInfo; + private static String NO_REFERENCE_BIT_TEXT = "not reference"; + private static String REFERENCE_BIT_TEXT = "not reference"; + private static String OBJECT_INDEX_TEXT = "object index"; + private static String STRING_INDEX_TEXT = "string index"; + private static String TRAIT_INDEX_TEXT = "trait index"; public Amf3InputStream(MemoryInputStream is) { this.is = is; @@ -59,16 +68,6 @@ public class Amf3InputStream extends InputStream { } } - private void renameLastDump(String newname) { - if (dumpInfo != null) { - if (!dumpInfo.getChildInfos().isEmpty()) { - DumpInfo di = dumpInfo.getChildInfos().get(dumpInfo.getChildInfos().size() - 1); - di.name = newname; - } - } - - } - public void endDumpLevelUntil(DumpInfo di) { if (di != null) { while (dumpInfo != null && dumpInfo != di) { @@ -135,7 +134,7 @@ public class Amf3InputStream extends InputStream { newDumpLevel(name, "DOUBLE"); long lval = readLong(); double ret = Double.longBitsToDouble(lval); - endDumpLevel(ret); + endDumpLevel(EcmaScript.toString(ret)); return ret; } @@ -146,6 +145,68 @@ public class Amf3InputStream extends InputStream { return val; } + private void renameU29O_ref() { + renameU29("U29O-ref", REFERENCE_BIT_TEXT, OBJECT_INDEX_TEXT); + } + + private void renameU29S_ref() { + renameU29("U29S-ref", REFERENCE_BIT_TEXT, STRING_INDEX_TEXT); + } + + private void renameU29Traits_ref() { + renameU29("U29O-traits-ref", NO_REFERENCE_BIT_TEXT, "trait reference", TRAIT_INDEX_TEXT); + } + + private void renameLastDump(String wholeName) { + if (dumpInfo != null) { + if (!dumpInfo.getChildInfos().isEmpty()) { + DumpInfo u29DumpInfo = dumpInfo.getChildInfos().get(dumpInfo.getChildInfos().size() - 1); + u29DumpInfo.name = wholeName; + } + } + } + + private void setDumpInfoType(String type) { + if (dumpInfo != null) { + dumpInfo.type = type; + } + } + + private void renameU29(String wholeName, String name1, String... names) { + List bitNames = new ArrayList<>(); + bitNames.add(name1); + bitNames.addAll(Arrays.asList(names)); + + String restName = bitNames.remove(bitNames.size() - 1); + + if (bitNames.size() > 6) { + throw new RuntimeException("Renaming more than 6 bits in U29 is not supported"); + } + + if (dumpInfo != null) { + if (!dumpInfo.getChildInfos().isEmpty()) { + DumpInfo u29DumpInfo = dumpInfo.getChildInfos().get(dumpInfo.getChildInfos().size() - 1); + u29DumpInfo.name = wholeName; + long lastBytePos = u29DumpInfo.startByte + u29DumpInfo.lengthBytes - 1; //last byte of U29 is least significant (U29 is big endian) + + int remainingBitLength = ((int) u29DumpInfo.lengthBytes) * 8 - bitNames.size(); + long u29val = ((long) (Long) u29DumpInfo.previewValue); + + DumpInfo restDumpInfo = new DumpInfo(restName, "UB(" + remainingBitLength + ")", u29val >> bitNames.size(), lastBytePos, 0, u29DumpInfo.lengthBytes, remainingBitLength); + restDumpInfo.parent = u29DumpInfo; + u29DumpInfo.getChildInfos().add(restDumpInfo); + + for (int i = bitNames.size() - 1; i >= 0; i--) { + int bitVal = (int) ((u29val >> i) & 1); + DumpInfo bitDumpInfo = new DumpInfo(bitNames.get(i), "bit", bitVal, lastBytePos, 7 - i, 1, 1); + bitDumpInfo.parent = u29DumpInfo; + u29DumpInfo.getChildInfos().add(bitDumpInfo); + } + + } + } + } + public long readS29(String name) throws IOException { newDumpLevel(name, "S29"); long val = signExtend(readU29Internal(), 29); @@ -191,31 +252,34 @@ public class Amf3InputStream extends InputStream { throw new EndOfStreamException(); } String retString = new String(buf, "UTF-8"); - endDumpLevel(retString); + endDumpLevel("\"" + Helper.escapeActionScriptString(retString) + "\""); return retString; } public String readUtf8Vr(String name, List stringTable) throws IOException { + newDumpLevel(name, "UTF-8-vr"); long u = readU29("U29S"); int stringNoRefFlag = (int) (u & 1); + String retString; if (stringNoRefFlag == 1) { - renameLastDump("U29S-value"); + renameU29("U29S-value", NO_REFERENCE_BIT_TEXT, "byte length"); long byteLength = u >> 1; //TODO: long strings, int is not enough for them - String retString = readUtf8Char("characters", byteLength); + retString = readUtf8Char("characters", byteLength); if (byteLength > 0) { stringTable.add(retString); } LOGGER.log(Level.FINE, "Read string: \"{0}\"", retString); - return retString; } else { //flag==0 - renameLastDump("U29S-ref"); + renameU29S_ref(); int stringRefTableIndex = (int) (u >> 1); - String retString = stringTable.get(stringRefTableIndex); + retString = stringTable.get(stringRefTableIndex); LOGGER.log(Level.FINE, "Read string: reference({0}):" + retString, stringRefTableIndex); - return retString; } + endDumpLevel("\"" + Helper.escapeActionScriptString(retString) + "\""); + return retString; + } private int readInternal() throws IOException { @@ -253,26 +317,31 @@ public class Amf3InputStream extends InputStream { switch (marker) { case Marker.UNDEFINED: renameLastDump("undefined-marker"); + setDumpInfoType("undefined-type"); LOGGER.log(Level.FINE, "Read value: undefined"); result = BasicType.UNDEFINED; break; case Marker.NULL: renameLastDump("null-marker"); + setDumpInfoType("null-type"); LOGGER.log(Level.FINE, "Read value: null"); result = BasicType.NULL; break; case Marker.FALSE: renameLastDump("false-marker"); + setDumpInfoType("false-type"); LOGGER.log(Level.FINE, "Read value: false"); result = Boolean.FALSE; break; case Marker.TRUE: renameLastDump("true-marker"); + setDumpInfoType("true-type"); LOGGER.log(Level.FINE, "Read value: true"); result = Boolean.TRUE; break; case Marker.INTEGER: renameLastDump("integer-marker"); + setDumpInfoType("integer-type"); LOGGER.log(Level.FINE, "Read value: integer"); long ival = readS29("intValue"); LOGGER.log(Level.FINER, "Integer value: {0}", ival); @@ -280,6 +349,7 @@ public class Amf3InputStream extends InputStream { break; case Marker.DOUBLE: renameLastDump("double-marker"); + setDumpInfoType("double-type"); LOGGER.log(Level.FINE, "Read value: double"); double dval = readDouble("doubleValue"); LOGGER.log(Level.FINER, "Double value: {0}", "" + dval); @@ -287,6 +357,7 @@ public class Amf3InputStream extends InputStream { break; case Marker.STRING: renameLastDump("string-marker"); + setDumpInfoType("string-type"); LOGGER.log(Level.FINE, "Read value: string"); String sval = readUtf8Vr("stringValue", stringTable); LOGGER.log(Level.FINER, "String value: {0}", sval); @@ -294,11 +365,12 @@ public class Amf3InputStream extends InputStream { break; case Marker.XML_DOC: renameLastDump("xml-doc-marker"); + setDumpInfoType("xml-doc-type"); LOGGER.log(Level.FINE, "Read value: xml_doc"); long xmlDocU29 = readU29("U29"); int xmlDocNoRefFlag = (int) (xmlDocU29 & 1); if (xmlDocNoRefFlag == 1) { - renameLastDump("U29X-value"); + renameU29("U29X-value", NO_REFERENCE_BIT_TEXT, "byte length"); long byteLength = xmlDocU29 >> 1; String xval = readUtf8Char("characters", byteLength); LOGGER.log(Level.FINER, "XmlDoc value: {0}", xval); @@ -306,7 +378,7 @@ public class Amf3InputStream extends InputStream { objectTable.add(retXmlDoc); result = retXmlDoc; } else { - renameLastDump("U29O-ref"); + renameU29O_ref(); int refIndexXmlDoc = (int) (xmlDocU29 >> 1); LOGGER.log(Level.FINER, "XmlDoc value: reference({0})", refIndexXmlDoc); result = objectTable.get(refIndexXmlDoc); //What if it's not XmlRef? @@ -314,11 +386,12 @@ public class Amf3InputStream extends InputStream { break; case Marker.DATE: renameLastDump("date-marker"); + setDumpInfoType("date-type"); LOGGER.log(Level.FINE, "Read value: date"); long dateU29 = readU29("U29"); int dateNoRefFlag = (int) (dateU29 & 1); if (dateNoRefFlag == 1) { - renameLastDump("U29D-value"); + renameU29("U29D-value", NO_REFERENCE_BIT_TEXT, "unused"); //remaining bits of dateU29 are not used double dtval = readDouble("date-time"); DateType retDate = new DateType(dtval); @@ -326,7 +399,7 @@ public class Amf3InputStream extends InputStream { objectTable.add(retDate); result = retDate; } else { - renameLastDump("U29O-ref"); + renameU29O_ref(); int refIndexDate = (int) (dateU29 >> 1); LOGGER.log(Level.FINER, "Date value: reference({0})", refIndexDate); result = objectTable.get(refIndexDate); //What if it's not Date? @@ -334,18 +407,19 @@ public class Amf3InputStream extends InputStream { break; case Marker.ARRAY: renameLastDump("array-marker"); + setDumpInfoType("array-type"); LOGGER.log(Level.FINE, "Read value: array"); long arrayU29 = readU29("U29"); int arrayNoRefFlag = (int) (arrayU29 & 1); if (arrayNoRefFlag == 1) { - renameLastDump("U29A-value"); + renameU29("U29A-value", NO_REFERENCE_BIT_TEXT, "dense count"); int denseCount = (int) (arrayU29 >> 1); LOGGER.log(Level.FINEST, "Array value: denseCount={0}", new Object[]{denseCount}); List> assocPart = new ArrayList<>(); List densePart = new ArrayList<>(); ArrayType retArray = new ArrayType(densePart, assocPart); objectTable.add(retArray); //add before processing elements which may reference this - + newDumpLevel("associativeValues", "assoc-value"); while (true) { String key = readUtf8Vr("key", stringTable); if (key.isEmpty()) { @@ -361,8 +435,10 @@ public class Amf3InputStream extends InputStream { } } } + endDumpLevel(); LOGGER.log(Level.FINEST, "Array value: assocSize={0}", new Object[]{assocPart.size()}); + newDumpLevel("denseValues", "value-type[]"); for (int i = 0; i < denseCount; i++) { try { densePart.add(readValue("denseValue", serializers, objectTable, traitsTable, stringTable)); @@ -374,11 +450,12 @@ public class Amf3InputStream extends InputStream { throw new NoSerializerExistsException(nse.getClassName(), retArray, nse); } } + endDumpLevel(); LOGGER.log(Level.FINER, "Array value: dense_size={0},assocSize={1}", new Object[]{densePart.size(), assocPart.size()}); result = retArray; } else { - renameLastDump("U29O-ref"); + renameU29O_ref(); int refIndexArray = (int) (arrayU29 >> 1); LOGGER.log(Level.FINER, "Array value: reference({0})", refIndexArray); result = objectTable.get(refIndexArray); //What if it's not Array? @@ -387,6 +464,7 @@ public class Amf3InputStream extends InputStream { case Marker.OBJECT: renameLastDump("object-marker"); + setDumpInfoType("object-type"); LOGGER.log(Level.FINE, "Read value: object"); long objectU29 = readU29("U29"); int objectNoRefFlag = (int) (objectU29 & 1); @@ -397,15 +475,16 @@ public class Amf3InputStream extends InputStream { int objectTraitsExtFlag = (int) ((objectU29 >> 2) & 1); ObjectType retObjectType; if (objectTraitsExtFlag == 1) { - renameLastDump("U29O-traits-ext"); + renameU29("U29O-traits-ext", NO_REFERENCE_BIT_TEXT, "not trait reference", "externalized traits", "unused"); String className = readUtf8Vr("className", stringTable); if (!serializers.containsKey(className)) { throw new NoSerializerExistsException(className, new ObjectType(new Traits(className, false, new ArrayList<>()), (byte[]) null, new ArrayList<>()), null); } - + newDumpLevel("serializedData", "U8[]"); MonitoredInputStream mis = new MonitoredInputStream(is); List> serMembers = serializers.get(className).readObject(className, mis); byte serData[] = mis.getReadData(); + endDumpLevel(); Traits unserTraits = new Traits(className, false, new ArrayList<>()); retObjectType = new ObjectType(unserTraits, serData, serMembers); @@ -414,7 +493,7 @@ public class Amf3InputStream extends InputStream { result = retObjectType; break markerswitch; } else { - renameLastDump("U29O-traits"); + renameU29("U29O-traits", NO_REFERENCE_BIT_TEXT, "not trait reference", "not externalized traits", "dynamic", "sealed count"); int dynamicFlag = (int) ((objectU29 >> 3) & 1); int numSealed = (int) (objectU29 >> 4); LOGGER.log(Level.FINEST, "object dynamicFlag:{0}", dynamicFlag); @@ -422,14 +501,19 @@ public class Amf3InputStream extends InputStream { String className = readUtf8Vr("className", stringTable); LOGGER.log(Level.FINEST, "object className:{0}", className); List sealedMemberNames = new ArrayList<>(); - for (int i = 0; i < numSealed; i++) { - sealedMemberNames.add(readUtf8Vr("sealedMemberName", stringTable)); + if (numSealed > 0) { + newDumpLevel("sealedMemberNames", "UTF-8-vr[]"); + + for (int i = 0; i < numSealed; i++) { + sealedMemberNames.add(readUtf8Vr("sealedMemberName", stringTable)); + } + endDumpLevel(); } traits = new Traits(className, dynamicFlag == 1, sealedMemberNames); traitsTable.add(traits); } } else { - renameLastDump("U29O-traits-ref"); + renameU29Traits_ref(); int refIndexTraits = (int) (objectU29 >> 2); traits = traitsTable.get(refIndexTraits); LOGGER.log(Level.FINER, "Traits value: reference({0}) - traitsize={1}", new Object[]{refIndexTraits, traits.getSealedMemberNames().size()}); @@ -441,41 +525,60 @@ public class Amf3InputStream extends InputStream { objectTable.add(retObjectType); //add it before any subvalue can reference it List sealedMemberValues = new ArrayList<>(); NoSerializerExistsException error = null; - - for (int i = 0; i < traits.getSealedMemberNames().size(); i++) { - try { - sealedMemberValues.add(readValue("sealedMemberValue", serializers, objectTable, traitsTable, stringTable)); - } catch (NoSerializerExistsException nse) { - sealedMemberValues.add(nse.getIncompleteData()); - for (int j = i + 1; j < traits.getSealedMemberNames().size(); j++) { - sealedMemberValues.add(BasicType.UNKNOWN); + if (!traits.getSealedMemberNames().isEmpty()) { + newDumpLevel("sealedMemberValues", "value-type[]"); + for (int i = 0; i < traits.getSealedMemberNames().size(); i++) { + try { + sealedMemberValues.add(readValue("sealedMemberValue", serializers, objectTable, traitsTable, stringTable)); + } catch (NoSerializerExistsException nse) { + sealedMemberValues.add(nse.getIncompleteData()); + for (int j = i + 1; j < traits.getSealedMemberNames().size(); j++) { + sealedMemberValues.add(BasicType.UNKNOWN); + } + error = nse; + break; } - error = nse; - break; } + endDumpLevel(); } for (int i = 0; i < traits.getSealedMemberNames().size(); i++) { sealedMembers.add(new Pair<>(traits.getSealedMemberNames().get(i), sealedMemberValues.get(i))); } if (traits.isDynamic()) { + newDumpLevel("dynamicMembers", "dynamic-member[]"); String dynamicMemberName; - while (!(dynamicMemberName = readUtf8Vr("dynamicMemberName", stringTable)).isEmpty()) { + while (!(dynamicMemberName = readUtf8Vr("name", stringTable)).isEmpty()) { try { - Object dynamicMemberValue = readValue("dynamicMemberValue", serializers, objectTable, traitsTable, stringTable); + Object dynamicMemberValue = readValue("value", serializers, objectTable, traitsTable, stringTable); dynamicMembers.add(new Pair<>(dynamicMemberName, dynamicMemberValue)); } catch (NoSerializerExistsException nse) { dynamicMembers.add(new Pair<>(dynamicMemberName, nse.getIncompleteData())); throw new NoSerializerExistsException(nse.getClassName(), retObjectType, nse); + } finally { + //group dumpInfo to one sub "dynamic-member" + if (dumpInfo != null) { + DumpInfo valueDumpInfo = dumpInfo.getChildInfos().remove(dumpInfo.getChildInfos().size() - 1); + DumpInfo nameDumpInfo = dumpInfo.getChildInfos().remove(dumpInfo.getChildInfos().size() - 1); + DumpInfo memberDumpInfo = new DumpInfo("member", "dynamic-member", "", nameDumpInfo.startByte, nameDumpInfo.lengthBytes + valueDumpInfo.lengthBytes); + memberDumpInfo.getChildInfos().add(nameDumpInfo); + memberDumpInfo.getChildInfos().add(valueDumpInfo); + memberDumpInfo.parent = dumpInfo; + nameDumpInfo.parent = memberDumpInfo; + valueDumpInfo.parent = memberDumpInfo; + memberDumpInfo.previewValue = "" + nameDumpInfo.previewValue + (valueDumpInfo.previewValue != null ? " : " + valueDumpInfo.previewValue : ""); + dumpInfo.getChildInfos().add(memberDumpInfo); + } } } renameLastDump("UTF-8-empty"); + endDumpLevel(); } LOGGER.log(Level.FINER, "Object value: dynamic={0},className={1},sealedSize={2},dynamicSize={3}", new Object[]{traits.isDynamic(), traits.getClassName(), sealedMembers.size(), dynamicMembers.size()}); result = retObjectType; } else { - renameLastDump("U29O-ref"); + renameU29O_ref(); int refIndexObject = (int) (objectU29 >> 1); LOGGER.log(Level.FINER, "Object value: reference({0})", refIndexObject); result = objectTable.get(refIndexObject); @@ -483,11 +586,12 @@ public class Amf3InputStream extends InputStream { break; case Marker.XML: renameLastDump("xml-marker"); + setDumpInfoType("xml-type"); LOGGER.log(Level.FINE, "Read value: xml"); long xmlU29 = readU29("U29"); int xmlNoRefFlag = (int) (xmlU29 & 1); if (xmlNoRefFlag == 1) { - renameLastDump("U29X-value"); + renameU29("U29X-value", NO_REFERENCE_BIT_TEXT, "byte length"); long byteLength = (xmlU29 >> 1); String xString = readUtf8Char("characters", byteLength); XmlType retXmlType = new XmlType(xString); @@ -495,7 +599,7 @@ public class Amf3InputStream extends InputStream { objectTable.add(retXmlType); result = retXmlType; } else { - renameLastDump("U29O-ref"); + renameU29O_ref(); int refIndexXml = (int) (xmlU29 >> 1); LOGGER.log(Level.FINER, "XML value: reference({0})", refIndexXml); result = objectTable.get(refIndexXml); @@ -503,23 +607,26 @@ public class Amf3InputStream extends InputStream { break; case Marker.BYTE_ARRAY: renameLastDump("byte-array-marker"); + setDumpInfoType("bytearray-type"); LOGGER.log(Level.FINE, "Read value: bytearray"); long byteArrayU29 = readU29("U29"); int byteArrayNoRefFlag = (int) (byteArrayU29 & 1); if (byteArrayNoRefFlag == 1) { - renameLastDump("U29B-value"); + renameU29("U29B-value", NO_REFERENCE_BIT_TEXT, "byte array length"); int byteArrayLength = (int) (byteArrayU29 >> 1); + newDumpLevel("bytes", "U8[]"); byte byteArrayBuf[] = new byte[byteArrayLength]; if (is.read(byteArrayBuf) != byteArrayLength) { throw new EndOfStreamException(); } + endDumpLevel(); LOGGER.log(Level.FINER, "ByteArray value: bytes[{0}]", byteArrayLength); ByteArrayType retByteArrayType = new ByteArrayType(byteArrayBuf); objectTable.add(retByteArrayType); result = retByteArrayType; } else { - renameLastDump("U290-ref"); + renameU29O_ref(); int refIndexByteArray = (int) (byteArrayU29 >> 1); LOGGER.log(Level.FINER, "ByteArray value: reference({0})", refIndexByteArray); result = objectTable.get(refIndexByteArray); @@ -527,23 +634,26 @@ public class Amf3InputStream extends InputStream { break; case Marker.VECTOR_INT: renameLastDump("vector-int-marker"); + setDumpInfoType("vector-int-type"); LOGGER.log(Level.FINE, "Read value: vector_int"); long vectorIntU29 = readU29("U29"); int vectorIntNoRefFlag = (int) (vectorIntU29 & 1); if (vectorIntNoRefFlag == 1) { - renameLastDump("U29V-value"); + renameU29("U29V-value", NO_REFERENCE_BIT_TEXT, "item count"); int vectorIntCountItems = (int) (vectorIntU29 >> 1); int fixed = readU8("fixed"); List vals = new ArrayList<>(); + newDumpLevel("items", "S32[]"); for (int i = 0; i < vectorIntCountItems; i++) { - vals.add(readS32("value")); + vals.add(readS32("intValue")); } + endDumpLevel(); VectorIntType retVectorInt = new VectorIntType(fixed == 1, vals); LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}]", new Object[]{fixed, vectorIntCountItems}); objectTable.add(retVectorInt); result = retVectorInt; } else { - renameLastDump("U29O-ref"); + renameU29O_ref(); int refIndexVectorInt = (int) (vectorIntU29 >> 1); LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorInt); result = objectTable.get(refIndexVectorInt); @@ -551,23 +661,27 @@ public class Amf3InputStream extends InputStream { break; case Marker.VECTOR_UINT: renameLastDump("vector-uint-marker"); + setDumpInfoType("vector-uint-type"); + LOGGER.log(Level.FINE, "Read value: vector_uint"); long vectorUIntU29 = readU29("U29"); int vectorUIntNoRefFlag = (int) (vectorUIntU29 & 1); if (vectorUIntNoRefFlag == 1) { - renameLastDump("U29V-value"); + renameU29("U29V-value", NO_REFERENCE_BIT_TEXT, "item count"); int vectorUIntCountItems = (int) (vectorUIntU29 >> 1); int fixed = readU8("fixed"); List vals = new ArrayList<>(); + newDumpLevel("items", "U32[]"); for (int i = 0; i < vectorUIntCountItems; i++) { - vals.add(readU32("value")); + vals.add(readU32("uintValue")); } + endDumpLevel(); VectorUIntType retVectorUInt = new VectorUIntType(fixed == 1, vals); LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}]", new Object[]{fixed, vectorUIntCountItems}); objectTable.add(retVectorUInt); result = retVectorUInt; } else { - renameLastDump("U29O-ref"); + renameU29O_ref(); int refIndexVectorUInt = (int) (vectorUIntU29 >> 1); LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorUInt); result = objectTable.get(refIndexVectorUInt); @@ -575,23 +689,27 @@ public class Amf3InputStream extends InputStream { break; case Marker.VECTOR_DOUBLE: renameLastDump("vector-double-marker"); + setDumpInfoType("vector-double-type"); + LOGGER.log(Level.FINE, "Read value: vector_double"); long vectorDoubleU29 = readU29("U29"); int vectorDoubleNoRefFlag = (int) (vectorDoubleU29 & 1); if (vectorDoubleNoRefFlag == 1) { - renameLastDump("U29V-value"); + renameU29("U29V-value", NO_REFERENCE_BIT_TEXT, "item count"); int vectorDoubleCountItems = (int) (vectorDoubleU29 >> 1); int fixed = readU8("fixed"); List vals = new ArrayList<>(); + newDumpLevel("items", "DOUBLE[]"); for (int i = 0; i < vectorDoubleCountItems; i++) { - vals.add(readDouble("value")); + vals.add(readDouble("doubleValue")); } + endDumpLevel(); VectorDoubleType retVectorDouble = new VectorDoubleType(fixed == 1, vals); LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}]", new Object[]{fixed, vectorDoubleCountItems}); objectTable.add(retVectorDouble); result = retVectorDouble; } else { - renameLastDump("U29O-ref"); + renameU29O_ref(); int refIndexVectorDouble = (int) (vectorDoubleU29 >> 1); LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorDouble); result = objectTable.get(refIndexVectorDouble); @@ -599,16 +717,19 @@ public class Amf3InputStream extends InputStream { break; case Marker.VECTOR_OBJECT: renameLastDump("vector-object-marker"); + setDumpInfoType("vector-object-type"); + LOGGER.log(Level.FINE, "Read value: vector_object"); long vectorObjectU29 = readU29("U29"); int vectorObjectNoRefFlag = (int) (vectorObjectU29 & 1); if (vectorObjectNoRefFlag == 1) { - renameLastDump("U29V-value"); + renameU29("U29V-value", NO_REFERENCE_BIT_TEXT, "item count"); int vectorObjectCountItems = (int) (vectorObjectU29 >> 1); int fixed = readU8("fixed"); String objectTypeName = readUtf8Vr("object-type-name", stringTable); //uses "*" for any type List vals = new ArrayList<>(); NoSerializerExistsException error = null; + newDumpLevel("items", "value_type[]"); for (int i = 0; i < vectorObjectCountItems; i++) { try { vals.add(readValue("value", serializers, objectTable, traitsTable, stringTable)); @@ -621,6 +742,7 @@ public class Amf3InputStream extends InputStream { break; } } + endDumpLevel(); VectorObjectType retVectorObject = new VectorObjectType(fixed == 1, objectTypeName, vals); LOGGER.log(Level.FINER, "Vector value: fixed={0}, size={1}, typeName:{2}]", new Object[]{fixed, vectorObjectCountItems, objectTypeName}); objectTable.add(retVectorObject); @@ -629,7 +751,7 @@ public class Amf3InputStream extends InputStream { } result = retVectorObject; } else { - renameLastDump("U29O-ref"); + renameU29O_ref(); int refIndexVectorObject = (int) (vectorObjectU29 >> 1); LOGGER.log(Level.FINER, "Vector value: reference({0})", refIndexVectorObject); @@ -638,16 +760,19 @@ public class Amf3InputStream extends InputStream { break; case Marker.DICTIONARY: renameLastDump("dictionary-marker"); + setDumpInfoType("dictionary-type"); + long dictionaryObjectU29 = readU29("U29"); int dictionaryNoRefFlag = (int) (dictionaryObjectU29 & 1); if (dictionaryNoRefFlag == 1) { - renameLastDump("U29Dict-value"); + renameU29("U29Dict-value", NO_REFERENCE_BIT_TEXT, "entries count"); int numEntries = (int) (dictionaryObjectU29 >> 1); - int weakKeys = readU8("fixed"); + int weakKeys = readU8("weak keys"); List> data = new ArrayList<>(); DictionaryType retDictionary = new DictionaryType(weakKeys == 1, data); objectTable.add(retDictionary); NoSerializerExistsException error = null; + newDumpLevel("entries", ""); for (int i = 0; i < numEntries; i++) { Object key; Object val; @@ -673,12 +798,13 @@ public class Amf3InputStream extends InputStream { break; } } + endDumpLevel(); if (error != null) { throw new NoSerializerExistsException(error.getClassName(), retDictionary, error); } result = retDictionary; } else { - renameLastDump("U29O-ref"); + renameU29O_ref(); int refIndexDictionary = (int) (dictionaryObjectU29 >> 1); LOGGER.log(Level.FINER, "Dictionary value: reference({0})", refIndexDictionary); result = objectTable.get(refIndexDictionary); @@ -693,6 +819,29 @@ public class Amf3InputStream extends InputStream { return result; } + private static String valToPreviewString(Object v) { + if (v instanceof ObjectType) { + return "{...}"; + } + if (v instanceof ArrayType) { + return "[...]"; + } + if (v instanceof DictionaryType) { + return ""; + } + if (v instanceof BasicType) { + return ""; + } + if (v instanceof DateType) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS"); + return sdf.format(((DateType) v).toDate()); + } + if (v instanceof String) { + return "\"" + Helper.escapeActionScriptString((String) v) + "\""; + } + return EcmaScript.toString(v); + } + private class MonitoredInputStream extends InputStream { private final InputStream is; From 0d8e621a17a1ff8c73dcc728a2d9b5c63204a098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Tue, 19 Jul 2016 22:21:47 +0200 Subject: [PATCH 16/20] AMF3 importer refactoring AMFTools to AMF3 exporter --- libsrc/ffdec_lib/lexers/amf3.flex | 191 +++ .../jpexs/decompiler/flash/amf/amf3/Pair.java | 12 +- .../amf/amf3/types/AbstractVectorType.java | 4 +- .../flash/amf/amf3/types/ArrayType.java | 4 +- .../flash/amf/amf3/types/ByteArrayType.java | 4 +- .../flash/amf/amf3/types/DateType.java | 4 +- .../flash/amf/amf3/types/DictionaryType.java | 4 +- .../flash/amf/amf3/types/ObjectType.java | 4 +- .../amf/amf3/Amf3Exporter.java} | 88 +- .../importers/amf/amf3/Amf3Importer.java | 556 +++++++++ .../flash/importers/amf/amf3/Amf3Lexer.java | 1033 +++++++++++++++++ .../amf/amf3/Amf3ParseException.java | 30 + .../importers/amf/amf3/ParsedSymbol.java | 63 + .../flash/importers/amf/amf3/SymbolGroup.java | 34 + .../flash/importers/amf/amf3/SymbolType.java | 41 + .../flash/amf/amf3/Amf3OutputStreamTest.java | 12 - .../amf/amf3/parser/Amf3ImporterTest.java | 85 ++ libsrc/ffdec_lib/testdata/amf3/AmfTest.as | 3 + libsrc/ffdec_lib/testdata/amf3/AmfTest.swf | Bin 2287 -> 2302 bytes .../ffdec_lib/testdata/amf3/generated/all.bin | Bin 765 -> 765 bytes .../testdata/amf3/generated/custom.bin | 4 +- .../generated/noserializer_dictionary_key.bin | Bin 68 -> 68 bytes .../testdata/amf3/generated/simple.bin | 2 + 23 files changed, 2109 insertions(+), 69 deletions(-) create mode 100644 libsrc/ffdec_lib/lexers/amf3.flex rename libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/{amf/amf3/Amf3Tools.java => exporters/amf/amf3/Amf3Exporter.java} (80%) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Importer.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Lexer.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3ParseException.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/ParsedSymbol.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolGroup.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolType.java create mode 100644 libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/importers/amf/amf3/parser/Amf3ImporterTest.java create mode 100644 libsrc/ffdec_lib/testdata/amf3/generated/simple.bin diff --git a/libsrc/ffdec_lib/lexers/amf3.flex b/libsrc/ffdec_lib/lexers/amf3.flex new file mode 100644 index 000000000..7cbcd3fd2 --- /dev/null +++ b/libsrc/ffdec_lib/lexers/amf3.flex @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.amf.amf3; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +%% + +%public +%class Amf3Lexer +%final +%unicode +%char +%type ParsedSymbol +%throws Amf3ParseException + +%{ + + StringBuilder string = new StringBuilder(); + + private static String xmlTagName = ""; + + public int yychar() { + return yychar; + } + + private final Stack pushedBack = new Stack<>(); + + public int yyline() { + return yyline + 1; + } + + public void pushback(ParsedSymbol symb) { + pushedBack.push(symb); + last = null; + } + + ParsedSymbol last; + public ParsedSymbol lex() throws java.io.IOException, Amf3ParseException{ + ParsedSymbol ret = null; + if (!pushedBack.isEmpty()){ + ret = last = pushedBack.pop(); + } else { + ret = last = yylex(); + } + return ret; + } + +%} + +/* main character classes */ +LineTerminator = \r|\n|\r\n +InputCharacter = [^\r\n] + +WhiteSpace = {LineTerminator} | [ \t\f]+ + +/* comments */ +Comment = {TraditionalComment} | {EndOfLineComment} + +TraditionalComment = "/*" [^*] ~"*/" | "/*" "*"+ "/" +EndOfLineComment = "//" {InputCharacter}* {LineTerminator}? + + + +/* identifiers */ +Identifier = [:jletter:][:jletterdigit:]* + +Reference = "#" {Identifier} + +/* integer literals */ +DecIntegerLiteral = 0 | "-"? [1-9][0-9]* + +HexIntegerLiteral = 0 [xX] 0* {HexDigit} {1,8} +HexDigit = [0-9a-fA-F] + +OctIntegerLiteral = 0+ [1-3]? {OctDigit} {1,15} +OctDigit = [0-7] + +/* floating point literals */ +DoubleLiteral = "-"? ({FLit1}|{FLit2}|{FLit3}) {Exponent}? + +FLit1 = [0-9]+ \. [0-9]* +FLit2 = \. [0-9]+ +FLit3 = [0-9]+ +Exponent = [eE] [+-]? [0-9]+ + +/* string and character literals */ +StringCharacter = [^\r\n\"\\] + +%state STRING, CHARLITERAL + +%% + + { + + /* keywords */ + "null" { return new ParsedSymbol(SymbolGroup.GLOBALCONST, SymbolType.NULL, yytext()); } + "undefined" { return new ParsedSymbol(SymbolGroup.GLOBALCONST, SymbolType.UNDEFINED, yytext()); } + "false" { return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.FALSE, yytext()); } + "true" { return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.TRUE, yytext()); } + "unknown" { return new ParsedSymbol(SymbolGroup.UNKNOWN, SymbolType.UNKNOWN, yytext()); } + + + /* operators */ + + "{" { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.CURLY_OPEN, yytext()); } + "}" { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.CURLY_CLOSE, yytext()); } + "[" { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.BRACKET_OPEN, yytext()); } + "]" { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.BRACKET_CLOSE, yytext()); } + "," { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.COMMA, yytext()); } + ":" { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.COLON, yytext()); } + + /* string literal */ + \" { + string.setLength(0); + yybegin(STRING); + } + + + /* numeric literals */ + + {DecIntegerLiteral} { return new ParsedSymbol(SymbolGroup.INTEGER, SymbolType.INTEGER, Long.parseLong((yytext()))); } + + {HexIntegerLiteral} { return new ParsedSymbol(SymbolGroup.INTEGER, SymbolType.INTEGER, Long.parseLong(yytext().substring(2), 16)); } + + {OctIntegerLiteral} { return new ParsedSymbol(SymbolGroup.INTEGER, SymbolType.INTEGER, Long.parseLong(yytext(), 8)); } + + {DoubleLiteral} { return new ParsedSymbol(SymbolGroup.DOUBLE, SymbolType.DOUBLE, Double.parseDouble((yytext()))); } + + /* comments */ + {Comment} { /*ignore*/ } + + {LineTerminator} { yyline++;} + /* whitespace */ + {WhiteSpace} { /*ignore*/ } + + /* identifiers */ + {Reference} { return new ParsedSymbol(SymbolGroup.IDENTIFIER, SymbolType.REFERENCE, yytext().substring(1)); } + {Identifier} { return new ParsedSymbol(SymbolGroup.IDENTIFIER, SymbolType.IDENTIFIER, yytext()); } +} + + { + \" { + yybegin(YYINITIAL); + // length also includes the trailing quote + return new ParsedSymbol(SymbolGroup.STRING, SymbolType.STRING, string.toString()); + } + + {StringCharacter}+ { string.append(yytext()); } + + /* escape sequences */ + "\\b" { string.append('\b'); } + "\\t" { string.append('\t'); } + "\\n" { string.append('\n'); } + "\\f" { string.append('\f'); } + "\\r" { string.append('\r'); } + "\\\"" { string.append('\"'); } + "\\'" { string.append('\''); } + "\\\\" { string.append('\\'); } + \\x{HexDigit}{2} { char val = (char) Integer.parseInt(yytext().substring(2), 16); + string.append(val); } + \\u{HexDigit}{4} { char val = (char) Integer.parseInt(yytext().substring(2), 16); + string.append(val); } + \\{OctDigit}{3} { char val = (char) Integer.parseInt(yytext().substring(1), 8); + string.append(val); } + + /* escape sequences */ + + \\. { string.append('\\'); /*illegal escape sequence*/ } + {LineTerminator} { yybegin(YYINITIAL); yyline++;} +} + +/* error fallback */ +[^] { } +<> { return new ParsedSymbol(SymbolGroup.EOF, SymbolType.EOF, null); } + diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Pair.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Pair.java index 021814ec2..81e7dd7b1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Pair.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Pair.java @@ -2,8 +2,8 @@ package com.jpexs.decompiler.flash.amf.amf3; public class Pair { - private final T1 first; - private final T2 second; + private T1 first; + private T2 second; public Pair(T1 first, T2 second) { this.first = first; @@ -17,4 +17,12 @@ public class Pair { public T2 getSecond() { return second; } + + public void setSecond(T2 second) { + this.second = second; + } + + public void setFirst(T1 first) { + this.first = first; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java index 905236454..8e87f4009 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; import java.util.ArrayList; import java.util.List; import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; @@ -34,7 +34,7 @@ public abstract class AbstractVectorType implements WithSubValues { @Override public String toString() { - return Amf3Tools.amfToString(this); + return Amf3Exporter.amfToString(this); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java index 9c9813836..411d90003 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; import com.jpexs.decompiler.flash.amf.amf3.Pair; import com.jpexs.helpers.Helper; import java.util.ArrayList; @@ -27,7 +27,7 @@ public class ArrayType implements WithSubValues { @Override public String toString() { - return Amf3Tools.amfToString(this); + return Amf3Exporter.amfToString(this); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java index 2e0c57445..57699226c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; public class ByteArrayType { @@ -16,7 +16,7 @@ public class ByteArrayType { @Override public String toString() { - return Amf3Tools.amfToString(this); + return Amf3Exporter.amfToString(this); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java index 6458b9dff..50e73f215 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; import java.util.Date; public class DateType { @@ -21,7 +21,7 @@ public class DateType { @Override public String toString() { - return Amf3Tools.amfToString(this); + return Amf3Exporter.amfToString(this); } public Date toDate() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java index 6b2cc4cb2..bcae93132 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; import com.jpexs.decompiler.flash.amf.amf3.Pair; import java.util.ArrayList; import java.util.List; @@ -32,7 +32,7 @@ public class DictionaryType implements WithSubValues { @Override public String toString() { - return Amf3Tools.amfToString(this); + return Amf3Exporter.amfToString(this); } public boolean hasWeakKeys() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java index fd0deb2ff..4333a0f9c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; import com.jpexs.decompiler.flash.amf.amf3.Pair; import com.jpexs.decompiler.flash.amf.amf3.Traits; import com.jpexs.helpers.Helper; @@ -81,7 +81,7 @@ public class ObjectType implements WithSubValues { @Override public String toString() { - return Amf3Tools.amfToString(this); + return Amf3Exporter.amfToString(this); } public void setSerializedData(byte[] serializedData) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Tools.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/amf/amf3/Amf3Exporter.java similarity index 80% rename from libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Tools.java rename to libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/amf/amf3/Amf3Exporter.java index 899db6ae1..afe62a3c2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Tools.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/amf/amf3/Amf3Exporter.java @@ -1,5 +1,7 @@ -package com.jpexs.decompiler.flash.amf.amf3; +package com.jpexs.decompiler.flash.exporters.amf.amf3; +import com.jpexs.decompiler.flash.amf.amf3.Pair; +import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; import com.jpexs.decompiler.flash.amf.amf3.types.ArrayType; import com.jpexs.decompiler.flash.amf.amf3.types.XmlType; import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; @@ -9,6 +11,7 @@ import com.jpexs.decompiler.flash.amf.amf3.types.ByteArrayType; import com.jpexs.decompiler.flash.amf.amf3.types.DateType; import com.jpexs.decompiler.flash.amf.amf3.types.DictionaryType; import com.jpexs.decompiler.flash.amf.amf3.types.BasicType; +import com.jpexs.decompiler.flash.ecma.EcmaScript; import com.jpexs.helpers.Helper; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -18,20 +21,20 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class Amf3Tools { +public class Amf3Exporter { /** * Converts AMF value to something human-readable. * - * @param obj + * @param amfValue * @return */ - public static String amfToString(Object obj) { + public static String amfToString(Object amfValue) { Map refCount = new HashMap<>(); List objectList = new ArrayList<>(); Map objectAlias = new HashMap<>(); - populateObjects(obj, refCount, objectList, objectAlias); - return amfToString(new ArrayList<>(), 0, obj, refCount, objectAlias); + populateObjects(amfValue, refCount, objectList, objectAlias); + return amfToString(new ArrayList<>(), 0, amfValue, refCount, objectAlias); } /** @@ -89,7 +92,7 @@ public class Amf3Tools { return "\"" + Helper.escapeActionScriptString((String) object) + "\""; } if (((List) Arrays.asList(Long.class, Double.class, Boolean.class)).contains(object.getClass())) { - return object.toString(); + return EcmaScript.toString(object); } if (object instanceof BasicType) { @@ -105,7 +108,7 @@ public class Amf3Tools { } processedObjects.add(object); - String addId = refCount > 1 ? indent(level + 1) + "\"id\": \"" + objectAlias.get(object) + "\"\r\n" : ""; + String addId = refCount > 1 ? indent(level + 1) + "\"id\": \"" + objectAlias.get(object) + "\",\r\n" : ""; if (object instanceof AbstractVectorType) { AbstractVectorType avt = (AbstractVectorType) object; @@ -134,7 +137,7 @@ public class Amf3Tools { if (serData == null) { ret.append(indent(level + 1)).append("\"serialized\": unknown\r\n"); } else { - ret.append(indent(level + 1)).append("\"serialized\": 0x").append(javax.xml.bind.DatatypeConverter.printHexBinary(serData)).append(",\r\n"); + ret.append(indent(level + 1)).append("\"serialized\": \"").append(javax.xml.bind.DatatypeConverter.printHexBinary(serData)).append("\",\r\n"); if (!ot.getSerializedMembers().isEmpty()) { ret.append(indent(level + 1)).append("\"unserializedMembers\": {\r\n"); for (int i = 0; i < ot.getSerializedMembers().size(); i++) { @@ -152,41 +155,44 @@ public class Amf3Tools { } } else { ret.append(indent(level + 1)).append("\"dynamic\": ").append(ot.isDynamic()).append(",\r\n"); - if (!ot.getSealedMembers().isEmpty()) { - ret.append(indent(level + 1)).append("\"sealedMembers\": {\r\n"); - for (int i = 0; i < ot.getSealedMembers().size(); i++) { - Pair member = ot.getSealedMembers().get(i); - ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 1, member.getSecond(), referenceCount, objectAlias)); - if (i < ot.getSealedMembers().size() - 1) { - ret.append(",\r\n"); - } else { - ret.append("\r\n"); - } + //if (!ot.getSealedMembers().isEmpty()) { + ret.append(indent(level + 1)).append("\"sealedMembers\": {\r\n"); + for (int i = 0; i < ot.getSealedMembers().size(); i++) { + Pair member = ot.getSealedMembers().get(i); + ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 1, member.getSecond(), referenceCount, objectAlias)); + if (i < ot.getSealedMembers().size() - 1) { + ret.append(",\r\n"); + } else { + ret.append("\r\n"); } - ret.append(indent(level + 1)).append("}"); - if (!ot.getDynamicMembers().isEmpty()) { + } + ret.append(indent(level + 1)).append("}"); + //if (!ot.getDynamicMembers().isEmpty()) { + ret.append(","); + //} + ret.append("\r\n"); + //} + //if (!ot.getDynamicMembers().isEmpty()) { + ret.append(indent(level + 1)).append("\"dynamicMembers\": {\r\n"); + for (int i = 0; i < ot.getDynamicMembers().size(); i++) { + Pair member = ot.getDynamicMembers().get(i); + ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)); + ret.append(":"); + ret.append(amfToString(processedObjects, level + 2, member.getSecond(), referenceCount, objectAlias)); + if (i < ot.getDynamicMembers().size() - 1) { ret.append(","); } ret.append("\r\n"); } - if (!ot.getDynamicMembers().isEmpty()) { - ret.append(indent(level + 1)).append("\"dynamicMembers\": {\r\n"); - for (int i = 0; i < ot.getDynamicMembers().size(); i++) { - Pair member = ot.getDynamicMembers().get(i); - ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 2, member.getSecond(), referenceCount, objectAlias)); - if (i < ot.getDynamicMembers().size() - 1) { - ret.append(","); - } - ret.append("\r\n"); - } - ret.append(indent(level + 1)).append("}\r\n"); - } + ret.append(indent(level + 1)).append("}\r\n"); + //} } ret.append(indent(level)).append("}"); } else if (object instanceof ArrayType) { ArrayType at = (ArrayType) object; ret.append("{\r\n"); ret.append(indent(level + 1)).append("\"type\": \"Array\",\r\n"); + ret.append(addId); ret.append(indent(level + 1)).append("\"denseValues\": ["); for (int i = 0; i < at.getDenseValues().size(); i++) { @@ -196,7 +202,7 @@ public class Amf3Tools { ret.append(amfToString(processedObjects, level + 2, at.getDenseValues().get(i), referenceCount, objectAlias)); } ret.append("],\r\n"); - ret.append(indent(level + 1)).append("\"associativeValues\": ["); + ret.append(indent(level + 1)).append("\"associativeValues\": {"); if (!at.getAssociativeValues().isEmpty()) { ret.append("\r\n"); } @@ -211,15 +217,15 @@ public class Amf3Tools { if (!at.getAssociativeValues().isEmpty()) { ret.append(indent(level + 1)); } - ret.append("]\r\n"); + ret.append("}\r\n"); ret.append(indent(level)).append("}"); } else if (object instanceof DictionaryType) { DictionaryType dt = (DictionaryType) object; ret.append("{\r\n"); ret.append(indent(level + 1)).append("\"type\": \"Dictionary\",\r\n"); ret.append(addId); - ret.append(indent(level + 1)).append("\"weakKeys\": " + dt.hasWeakKeys() + ",\r\n"); - ret.append(indent(level + 1)).append("\"value\": {\r\n"); + ret.append(indent(level + 1)).append("\"weakKeys\": ").append(dt.hasWeakKeys()).append(",\r\n"); + ret.append(indent(level + 1)).append("\"entries\": {\r\n"); for (int i = 0; i < dt.getPairs().size(); i++) { Pair pair = dt.getPairs().get(i); ret.append(indent(level + 1)).append(amfToString(processedObjects, level + 1, pair.getFirst(), referenceCount, objectAlias)).append(" : ").append(amfToString(processedObjects, level + 1, pair.getSecond(), referenceCount, objectAlias)); @@ -229,14 +235,14 @@ public class Amf3Tools { ret.append("\r\n"); } ret.append(indent(level + 1)).append("}\r\n"); - ret.append(indent(level) + "}"); + ret.append(indent(level)).append("}"); } else if (object instanceof ByteArrayType) { ByteArrayType ba = (ByteArrayType) object; byte data[] = ba.getData(); return "{\r\n" + indent(level + 1) + "\"type\": \"ByteArray\",\r\n" + addId - + indent(level + 1) + "\"value\": 0x" + javax.xml.bind.DatatypeConverter.printHexBinary(data) + "\r\n" + + indent(level + 1) + "\"value\": \"" + javax.xml.bind.DatatypeConverter.printHexBinary(data) + "\"\r\n" + indent(level) + "}"; } else if (object instanceof DateType) { DateType dt = (DateType) object; @@ -249,13 +255,13 @@ public class Amf3Tools { } else if (object instanceof XmlDocType) { return "{\r\n" - + indent(level + 1) + "\"type\": \"XML\",\r\n" + + indent(level + 1) + "\"type\": \"XMLDocument\",\r\n" + addId + indent(level + 1) + "\"value\": " + amfToString(processedObjects, level, ((XmlDocType) object).getData(), referenceCount, objectAlias) + "\r\n" + indent(level) + "}"; } else if (object instanceof XmlType) { return "{\r\n" - + indent(level + 1) + "\"type\": \"XMLDocument\",\r\n" + + indent(level + 1) + "\"type\": \"XML\",\r\n" + addId + indent(level + 1) + "\"value\": " + amfToString(processedObjects, level, ((XmlType) object).getData(), referenceCount, objectAlias) + "\r\n" + indent(level) + "}"; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Importer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Importer.java new file mode 100644 index 000000000..e21bd342f --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Importer.java @@ -0,0 +1,556 @@ +package com.jpexs.decompiler.flash.importers.amf.amf3; + +import com.jpexs.decompiler.flash.amf.amf3.Pair; +import com.jpexs.decompiler.flash.amf.amf3.Traits; +import com.jpexs.decompiler.flash.amf.amf3.types.ArrayType; +import com.jpexs.decompiler.flash.amf.amf3.types.BasicType; +import com.jpexs.decompiler.flash.amf.amf3.types.ByteArrayType; +import com.jpexs.decompiler.flash.amf.amf3.types.DateType; +import com.jpexs.decompiler.flash.amf.amf3.types.DictionaryType; +import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorDoubleType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorIntType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorObjectType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorUIntType; +import com.jpexs.decompiler.flash.amf.amf3.types.XmlDocType; +import com.jpexs.decompiler.flash.amf.amf3.types.XmlType; +import java.io.IOException; +import java.io.StringReader; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Amf3Importer { + + private Amf3Lexer lexer; + + private ParsedSymbol lex() throws IOException, Amf3ParseException { + ParsedSymbol ret = lexer.lex(); + return ret; + } + + private void pushback(ParsedSymbol s) { + lexer.pushback(s); + } + + private void expected(ParsedSymbol symb, int line, Object... expected) throws IOException, Amf3ParseException { + boolean found = false; + for (Object t : expected) { + if (symb.type == t) { + found = true; + } + if (symb.group == t) { + found = true; + } + } + if (!found) { + String expStr = ""; + boolean first = true; + for (Object e : expected) { + if (!first) { + expStr += " or "; + } + expStr += e; + first = false; + } + throw new Amf3ParseException("" + expStr + " expected but " + symb.type + " found", line); + } + } + + private ParsedSymbol expectedType(Object... type) throws IOException, Amf3ParseException { + ParsedSymbol symb = lex(); + expected(symb, lexer.yyline(), type); + return symb; + } + + private JsArray parseArray(Map objectTable) throws IOException, Amf3ParseException { + expectedType(SymbolType.BRACKET_OPEN); + List arrayVals = new ArrayList<>(); + ParsedSymbol s = lex(); + if (!s.isType(SymbolType.BRACKET_CLOSE)) { + pushback(s); + arrayVals.add(value(objectTable)); + s = lex(); + while (s.isType(SymbolType.COMMA)) { + arrayVals.add(value(objectTable)); + s = lex(); + } + pushback(s); + } + expectedType(SymbolType.BRACKET_CLOSE); + return new JsArray(arrayVals); + } + + private class JsArray { + + private List values = new ArrayList<>(); + + public JsArray() { + } + + public JsArray(List values) { + this.values = values; + } + + public void add(Object value) { + values.add(value); + } + + public List getValues() { + return values; + } + + } + + private class JsObject { + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("{"); + for (Pair p : values) { + sb.append(p.getFirst()).append(":").append("?").append(",\r\n"); + } + sb.append("}"); + return sb.toString(); + } + + private final List> values = new ArrayList<>(); + + public void add(Object key, Object value) { + values.add(new Pair<>(key, value)); + } + + public String getString(Object key) throws Amf3ParseException { + return (String) getRequired(key, "String"); + } + + public Boolean getBoolean(Object key) throws Amf3ParseException { + return (Boolean) getRequired(key, "Boolean"); + } + + public JsObject getJsObject(Object key) throws Amf3ParseException { + return (JsObject) getRequired(key, "JsObject"); + } + + public JsArray getJsArray(Object key) throws Amf3ParseException { + return (JsArray) getRequired(key, "JsArray"); + } + + public List getJsArrayOfObject(Object key) throws Amf3ParseException { + return getJsArray(key).getValues(); + } + + @SuppressWarnings("unchecked") + public List getJsArrayOfString(Object key) throws Amf3ParseException { + return (List) getJsArray(key, "String"); + } + + @SuppressWarnings("unchecked") + public List getJsArrayOfInt(Object key) throws Amf3ParseException { + return (List) getJsArray(key, "int"); + } + + @SuppressWarnings("unchecked") + + public List getJsArrayOfUint(Object key) throws Amf3ParseException { + return (List) getJsArray(key, "uint"); + } + + @SuppressWarnings("unchecked") + public List getJsArrayOfNumber(Object key) throws Amf3ParseException { + return (List) getJsArray(key, "Number"); + } + + public List getJsArray(Object key, String valueType) throws Amf3ParseException { + JsArray jsArr = (JsArray) getRequired(key, "JsArray"); + switch (valueType) { + case "String": + List stringList = new ArrayList<>(); + for (Object v : jsArr.getValues()) { + String sv = null; + if (v instanceof String) { + sv = (String) v; + } else { + throw new Amf3ParseException("Not String: " + v, 0); + } + stringList.add(sv); + } + return stringList; + case "int": + case "uint": + List longList = new ArrayList<>(); + for (Object v : jsArr.getValues()) { + Long lv = null; + if (v instanceof Long) { + lv = (Long) v; + } else { + throw new Amf3ParseException("Not an Integer value: " + v, 0); + } + if (valueType.equals("uint") && lv < 0) { + throw new Amf3ParseException("Not an Unsigned Integer value: " + v, 0); + } + longList.add(lv); + } + return longList; + case "Number": + List doubleList = new ArrayList<>(); + for (Object v : jsArr.getValues()) { + Double cv = null; + if (v instanceof Long) { + cv = (double) (long) (Long) v; + } else if (v instanceof Double) { + cv = (Double) v; + } else { + throw new Amf3ParseException("Not a Number: " + v, 0); + } + doubleList.add(cv); + } + return doubleList; + default: + throw new Amf3ParseException("Unsupported array value type: " + valueType, 0); + } + } + + public Long getLong(Object key) throws Amf3ParseException { + return (Long) getRequired(key, "Long"); + } + + public Object getRequired(Object key, String requiredType) throws Amf3ParseException { + if (!containsKey(key)) { + throw new Amf3ParseException("\"" + key + "\" is missing", 0); + } + Object val = get(key); + boolean typeMatches = true; + if (requiredType != null) { + switch (requiredType) { + case "String": + typeMatches = val instanceof String; + break; + case "Long": + typeMatches = val instanceof Long; + break; + case "JsObject": + typeMatches = val instanceof JsObject; + break; + case "JsArray": + typeMatches = val instanceof JsArray; + break; + case "Boolean": + typeMatches = val instanceof Boolean; + break; + } + } + if (!typeMatches) { + throw new Amf3ParseException("\"" + key + "\" value must be of type " + requiredType, 0); + } + return val; + } + + public boolean containsKey(Object key) { + for (Pair p : values) { + if (p.getFirst().equals(key)) { + return true; + } + } + return false; + } + + public Object get(Object key) { + for (Pair p : values) { + if (p.getFirst().equals(key)) { + return p.getSecond(); + } + } + return null; + } + + public void resolve(Object key, Map objectTable, boolean allowTypedObject) throws Amf3ParseException { + for (Pair p : values) { + if (p.getFirst().equals(key)) { + Object resolved = resolveObjects(p.getSecond(), objectTable, allowTypedObject); + p.setSecond(resolved); + return; + } + } + } + + public List stringKeys() { + List ret = new ArrayList<>(); + for (Pair p : values) { + if (p.getFirst() instanceof String) { + ret.add((String) p.getFirst()); + } + } + return ret; + } + + public List> getAll() { + return values; + } + + public List> getStringMapped() { + List> ret = new ArrayList<>(); + for (Pair p : values) { + if (p.getFirst() instanceof String) { + String keyStr = (String) p.getFirst(); + ret.add(new Pair<>(keyStr, p.getSecond())); + } + } + return ret; + } + + public Map getStringMap() { + Map ret = new HashMap<>(); + for (Pair p : values) { + if (p.getFirst() instanceof String) { + String keyStr = (String) p.getFirst(); + ret.put(keyStr, values); + } + } + return ret; + } + } + + private JsObject parseObject(Map objectTable) throws IOException, Amf3ParseException { + JsObject ret = new JsObject(); + + expectedType(SymbolType.CURLY_OPEN); + ParsedSymbol s = lex(); + if (!s.isType(SymbolType.CURLY_CLOSE)) { + pushback(s); + do { + Object key = value(objectTable); + expectedType(SymbolType.COLON); + Object value = value(objectTable); + ret.add(key, value); + if ("id".equals(key)) { + if (!(value instanceof String)) { + throw new Amf3ParseException("id must be string value", lexer.yyline()); + } + objectTable.put((String) value, BasicType.UNDEFINED); + } + s = lex(); + } while (s.isType(SymbolType.COMMA)); + } + pushback(s); + expectedType(SymbolType.CURLY_CLOSE); + return ret; + } + + private Object resolveObjects(Object object, Map objectTable, boolean allowTypedObject) throws Amf3ParseException { + Object resultObject = object; + if (object instanceof JsArray) { + JsArray jsa = (JsArray) object; + JsArray ret = new JsArray(); + for (int i = 0; i < jsa.values.size(); i++) { + ret.values.add(resolveObjects(jsa.values.get(i), objectTable, true)); + } + resultObject = ret; + } else if (object instanceof JsObject) { + if (allowTypedObject) { + JsObject typedObject = (JsObject) object; + if (typedObject.containsKey("type")) { + String typeStr = typedObject.getString("type"); + String id = typedObject.containsKey("id") ? typedObject.getString("id") : null; + switch (typeStr) { + case "Date": + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS"); + String dateStr = typedObject.getString("value"); + try { + resultObject = new DateType((double) sdf.parse(dateStr).getTime()); + } catch (ParseException ex) { + throw new Amf3ParseException("Invalid date format: " + dateStr, lexer.yyline()); + } + break; + case "XML": + resultObject = new XmlType(typedObject.getString("value")); + break; + case "XMLDocument": + resultObject = new XmlDocType(typedObject.getString("value")); + break; + case "Object": + String className = typedObject.getString("className"); + if (typedObject.containsKey("serialized")) { + //TODO + } else { + boolean dynamic = typedObject.getBoolean("dynamic"); + typedObject.resolve("sealedMembers", objectTable, false); + JsObject jsoSealed = typedObject.getJsObject("sealedMembers"); + + List> sealedMembers = jsoSealed.getStringMapped(); + typedObject.resolve("dynamicMembers", objectTable, false); + List> dynamicMembers = typedObject.getJsObject("dynamicMembers").getStringMapped(); + + List sealedMemberNames = new ArrayList<>(jsoSealed.stringKeys()); + resultObject = new ObjectType(new Traits(className, dynamic, sealedMemberNames), sealedMembers, dynamicMembers); + } + break; + case "Array": + typedObject.resolve("denseValues", objectTable, false); + List denseValues = typedObject.getJsArray("denseValues").getValues(); + typedObject.resolve("associativeValues", objectTable, false); + JsObject resolvedArr = typedObject.getJsObject("associativeValues"); + List> associativeValues = resolvedArr.getStringMapped(); + resultObject = new ArrayType(denseValues, associativeValues); + break; + + case "Vector": + boolean fixed = typedObject.getBoolean("fixed"); + String subtype = typedObject.getString("subtype"); + typedObject.resolve("values", objectTable, false); + switch (subtype) { + case "int": + resultObject = new VectorIntType(fixed, typedObject.getJsArrayOfInt("values")); + break; + case "uint": + resultObject = new VectorUIntType(fixed, typedObject.getJsArrayOfUint("values")); + break; + case "Number": + resultObject = new VectorDoubleType(fixed, typedObject.getJsArrayOfNumber("values")); + break; + default: + resultObject = new VectorObjectType(fixed, subtype, typedObject.getJsArrayOfObject("values")); + break; + } + break; + case "ByteArray": + try { + resultObject = new ByteArrayType(javax.xml.bind.DatatypeConverter.parseHexBinary(typedObject.getString("value"))); + } catch (IllegalArgumentException iex) { + throw new Amf3ParseException("Invalid hex byte sequence", lexer.yyline()); + } + break; + case "Dictionary": + boolean weakKeys = typedObject.getBoolean("weakKeys"); + typedObject.resolve("entries", objectTable, false); + List> entries = typedObject.getJsObject("entries").getAll(); + resultObject = new DictionaryType(weakKeys, entries); + break; + default: + throw new Amf3ParseException("Unknown object type: " + typeStr, lexer.yyline()); + } + if (id != null) { + objectTable.put(id, resultObject); + } + } + } else { //not allowTypeObject + JsObject jsObject = (JsObject) object; + for (Pair p : jsObject.getAll()) { + p.setFirst(resolveObjects(p.getFirst(), objectTable, true)); + p.setSecond(resolveObjects(p.getSecond(), objectTable, true)); + } + resultObject = jsObject; + } + } + return resultObject; + } + + private Object value(Map objectTable) throws IOException, Amf3ParseException { + ParsedSymbol s = lex(); + switch (s.type) { + case CURLY_OPEN: + pushback(s); + return parseObject(objectTable); + case BRACKET_OPEN: + pushback(s); + return parseArray(objectTable); + case STRING: + case DOUBLE: + case INTEGER: + return s.value; + case UNDEFINED: + return BasicType.UNDEFINED; + case NULL: + return BasicType.NULL; + case UNKNOWN: + return BasicType.UNKNOWN; + case TRUE: + return Boolean.TRUE; + case FALSE: + return Boolean.FALSE; + case REFERENCE: + String referencedId = (String) s.value; + return new ReferencedObjectType(referencedId); + default: + throw new Amf3ParseException("Unexpected symbol: " + s, lexer.yyline()); + } + } + + /** + * Deeply replace all ReferencedObjectType with the correct value + * + * @param object + * @param objectsTable + * @return + */ + private Object replaceReferences(Object object, Map objectsTable) throws Amf3ParseException { + if (object instanceof ReferencedObjectType) { + String key = ((ReferencedObjectType) object).key; + if (!objectsTable.containsKey(key)) { + throw new Amf3ParseException("Reference to undefined object: #" + key, 0); + } + return objectsTable.get(key); + } else if (object instanceof ObjectType) { + ObjectType ot = (ObjectType) object; + for (Pair p : ot.getSealedMembers()) { + p.setSecond(replaceReferences(p.getSecond(), objectsTable)); + } + for (Pair p : ot.getDynamicMembers()) { + p.setSecond(replaceReferences(p.getSecond(), objectsTable)); + } + } else if (object instanceof ArrayType) { + ArrayType at = (ArrayType) object; + for (Pair p : at.getAssociativeValues()) { + p.setSecond(replaceReferences(p.getSecond(), objectsTable)); + } + for (int i = 0; i < at.getDenseValues().size(); i++) { + at.getDenseValues().set(i, replaceReferences(at.getDenseValues().get(i), objectsTable)); + } + } else if (object instanceof VectorObjectType) { + VectorObjectType vot = (VectorObjectType) object; + for (int i = 0; i < vot.getValues().size(); i++) { + vot.getValues().set(i, replaceReferences(vot.getValues().get(i), objectsTable)); + } + } else if (object instanceof DictionaryType) { + DictionaryType dt = (DictionaryType) object; + for (Pair p : dt.getPairs()) { + p.setFirst(replaceReferences(p.getFirst(), objectsTable)); + p.setSecond(replaceReferences(p.getSecond(), objectsTable)); + } + } + + return object; + } + + private class ReferencedObjectType { + + private final String key; + + public ReferencedObjectType(String key) { + this.key = key; + } + + public String getKey() { + return key; + } + + @Override + public String toString() { + return "#" + key; + } + } + + public Object stringToAmf(String val) throws IOException, Amf3ParseException { + lexer = new Amf3Lexer(new StringReader(val)); + Map objectsTable = new HashMap<>(); + List references = new ArrayList<>(); + Object result = value(objectsTable); + Object resultResolved = resolveObjects(result, objectsTable, true); + Object resultNoRef = replaceReferences(resultResolved, objectsTable); + return resultNoRef; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Lexer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Lexer.java new file mode 100644 index 000000000..7052c61ef --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Lexer.java @@ -0,0 +1,1033 @@ +/* The following code was generated by JFlex 1.6.0 */ + +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.amf.amf3; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + + +/** + * This class is a scanner generated by + * JFlex 1.6.0 + * from the specification file D:/Dropbox/Programovani/JavaSE/FFDec/libsrc/ffdec_lib/lexers/amf3.flex + */ +public final class Amf3Lexer { + + /** This character denotes the end of file */ + public static final int YYEOF = -1; + + /** initial size of the lookahead buffer */ + private static final int ZZ_BUFFERSIZE = 16384; + + /** lexical states */ + public static final int YYINITIAL = 0; + public static final int STRING = 2; + public static final int CHARLITERAL = 4; + + /** + * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l + * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l + * at the beginning of a line + * l is of the form l = 2*k, k a non negative integer + */ + private static final int ZZ_LEXSTATE[] = { + 0, 0, 1, 1, 2, 2 + }; + + /** + * Translates characters to character classes + */ + private static final String ZZ_CMAP_PACKED = + "\11\7\1\3\1\2\1\54\1\55\1\1\16\7\4\0\1\3\1\0"+ + "\1\50\1\10\1\6\2\0\1\52\2\0\1\5\1\22\1\46\1\12"+ + "\1\20\1\4\1\11\3\16\4\17\2\13\1\47\6\0\4\15\1\21"+ + "\1\15\21\6\1\14\2\6\1\44\1\23\1\45\1\0\1\6\1\0"+ + "\1\33\1\51\1\15\1\27\1\30\1\31\2\6\1\32\1\6\1\37"+ + "\1\26\1\6\1\24\1\40\2\6\1\36\1\34\1\35\1\25\1\6"+ + "\1\41\1\53\2\6\1\42\1\0\1\43\1\0\6\7\1\56\32\7"+ + "\2\0\4\6\4\0\1\6\2\0\1\7\7\0\1\6\4\0\1\6"+ + "\5\0\27\6\1\0\37\6\1\0\u01ca\6\4\0\14\6\16\0\5\6"+ + "\7\0\1\6\1\0\1\6\21\0\160\7\5\6\1\0\2\6\2\0"+ + "\4\6\10\0\1\6\1\0\3\6\1\0\1\6\1\0\24\6\1\0"+ + "\123\6\1\0\213\6\1\0\5\7\2\0\236\6\11\0\46\6\2\0"+ + "\1\6\7\0\47\6\7\0\1\6\1\0\55\7\1\0\1\7\1\0"+ + "\2\7\1\0\2\7\1\0\1\7\10\0\33\6\5\0\3\6\15\0"+ + "\5\7\6\0\1\6\4\0\13\7\5\0\53\6\37\7\4\0\2\6"+ + "\1\7\143\6\1\0\1\6\10\7\1\0\6\7\2\6\2\7\1\0"+ + "\4\7\2\6\12\7\3\6\2\0\1\6\17\0\1\7\1\6\1\7"+ + "\36\6\33\7\2\0\131\6\13\7\1\6\16\0\12\7\41\6\11\7"+ + "\2\6\4\0\1\6\5\0\26\6\4\7\1\6\11\7\1\6\3\7"+ + "\1\6\5\7\22\0\31\6\3\7\104\0\1\6\1\0\13\6\67\0"+ + "\33\7\1\0\4\7\66\6\3\7\1\6\22\7\1\6\7\7\12\6"+ + "\2\7\2\0\12\7\1\0\7\6\1\0\7\6\1\0\3\7\1\0"+ + "\10\6\2\0\2\6\2\0\26\6\1\0\7\6\1\0\1\6\3\0"+ + "\4\6\2\0\1\7\1\6\7\7\2\0\2\7\2\0\3\7\1\6"+ + "\10\0\1\7\4\0\2\6\1\0\3\6\2\7\2\0\12\7\4\6"+ + "\7\0\1\6\5\0\3\7\1\0\6\6\4\0\2\6\2\0\26\6"+ + "\1\0\7\6\1\0\2\6\1\0\2\6\1\0\2\6\2\0\1\7"+ + "\1\0\5\7\4\0\2\7\2\0\3\7\3\0\1\7\7\0\4\6"+ + "\1\0\1\6\7\0\14\7\3\6\1\7\13\0\3\7\1\0\11\6"+ + "\1\0\3\6\1\0\26\6\1\0\7\6\1\0\2\6\1\0\5\6"+ + "\2\0\1\7\1\6\10\7\1\0\3\7\1\0\3\7\2\0\1\6"+ + "\17\0\2\6\2\7\2\0\12\7\1\0\1\6\17\0\3\7\1\0"+ + "\10\6\2\0\2\6\2\0\26\6\1\0\7\6\1\0\2\6\1\0"+ + "\5\6\2\0\1\7\1\6\7\7\2\0\2\7\2\0\3\7\10\0"+ + "\2\7\4\0\2\6\1\0\3\6\2\7\2\0\12\7\1\0\1\6"+ + "\20\0\1\7\1\6\1\0\6\6\3\0\3\6\1\0\4\6\3\0"+ + "\2\6\1\0\1\6\1\0\2\6\3\0\2\6\3\0\3\6\3\0"+ + "\14\6\4\0\5\7\3\0\3\7\1\0\4\7\2\0\1\6\6\0"+ + "\1\7\16\0\12\7\11\0\1\6\7\0\3\7\1\0\10\6\1\0"+ + "\3\6\1\0\27\6\1\0\12\6\1\0\5\6\3\0\1\6\7\7"+ + "\1\0\3\7\1\0\4\7\7\0\2\7\1\0\2\6\6\0\2\6"+ + "\2\7\2\0\12\7\22\0\2\7\1\0\10\6\1\0\3\6\1\0"+ + "\27\6\1\0\12\6\1\0\5\6\2\0\1\7\1\6\7\7\1\0"+ + "\3\7\1\0\4\7\7\0\2\7\7\0\1\6\1\0\2\6\2\7"+ + "\2\0\12\7\1\0\2\6\17\0\2\7\1\0\10\6\1\0\3\6"+ + "\1\0\51\6\2\0\1\6\7\7\1\0\3\7\1\0\4\7\1\6"+ + "\10\0\1\7\10\0\2\6\2\7\2\0\12\7\12\0\6\6\2\0"+ + "\2\7\1\0\22\6\3\0\30\6\1\0\11\6\1\0\1\6\2\0"+ + "\7\6\3\0\1\7\4\0\6\7\1\0\1\7\1\0\10\7\22\0"+ + "\2\7\15\0\60\6\1\7\2\6\7\7\4\0\10\6\10\7\1\0"+ + "\12\7\47\0\2\6\1\0\1\6\2\0\2\6\1\0\1\6\2\0"+ + "\1\6\6\0\4\6\1\0\7\6\1\0\3\6\1\0\1\6\1\0"+ + "\1\6\2\0\2\6\1\0\4\6\1\7\2\6\6\7\1\0\2\7"+ + "\1\6\2\0\5\6\1\0\1\6\1\0\6\7\2\0\12\7\2\0"+ + "\4\6\40\0\1\6\27\0\2\7\6\0\12\7\13\0\1\7\1\0"+ + "\1\7\1\0\1\7\4\0\2\7\10\6\1\0\44\6\4\0\24\7"+ + "\1\0\2\7\5\6\13\7\1\0\44\7\11\0\1\7\71\0\53\6"+ + "\24\7\1\6\12\7\6\0\6\6\4\7\4\6\3\7\1\6\3\7"+ + "\2\6\7\7\3\6\4\7\15\6\14\7\1\6\17\7\2\0\46\6"+ + "\1\0\1\6\5\0\1\6\2\0\53\6\1\0\u014d\6\1\0\4\6"+ + "\2\0\7\6\1\0\1\6\1\0\4\6\2\0\51\6\1\0\4\6"+ + "\2\0\41\6\1\0\4\6\2\0\7\6\1\0\1\6\1\0\4\6"+ + "\2\0\17\6\1\0\71\6\1\0\4\6\2\0\103\6\2\0\3\7"+ + "\40\0\20\6\20\0\125\6\14\0\u026c\6\2\0\21\6\1\0\32\6"+ + "\5\0\113\6\3\0\3\6\17\0\15\6\1\0\4\6\3\7\13\0"+ + "\22\6\3\7\13\0\22\6\2\7\14\0\15\6\1\0\3\6\1\0"+ + "\2\7\14\0\64\6\40\7\3\0\1\6\3\0\2\6\1\7\2\0"+ + "\12\7\41\0\3\7\2\0\12\7\6\0\130\6\10\0\51\6\1\7"+ + "\1\6\5\0\106\6\12\0\35\6\3\0\14\7\4\0\14\7\12\0"+ + "\12\7\36\6\2\0\5\6\13\0\54\6\4\0\21\7\7\6\2\7"+ + "\6\0\12\7\46\0\27\6\5\7\4\0\65\6\12\7\1\0\35\7"+ + "\2\0\13\7\6\0\12\7\15\0\1\6\130\0\5\7\57\6\21\7"+ + "\7\6\4\0\12\7\21\0\11\7\14\0\3\7\36\6\15\7\2\6"+ + "\12\7\54\6\16\7\14\0\44\6\24\7\10\0\12\7\3\0\3\6"+ + "\12\7\44\6\122\0\3\7\1\0\25\7\4\6\1\7\4\6\3\7"+ + "\2\6\11\0\300\6\47\7\25\0\4\7\u0116\6\2\0\6\6\2\0"+ + "\46\6\2\0\6\6\2\0\10\6\1\0\1\6\1\0\1\6\1\0"+ + "\1\6\1\0\37\6\2\0\65\6\1\0\7\6\1\0\1\6\3\0"+ + "\3\6\1\0\7\6\3\0\4\6\2\0\6\6\4\0\15\6\5\0"+ + "\3\6\1\0\7\6\16\0\5\7\30\0\1\54\1\54\5\7\20\0"+ + "\2\6\23\0\1\6\13\0\5\7\5\0\6\7\1\0\1\6\15\0"+ + "\1\6\20\0\15\6\3\0\33\6\25\0\15\7\4\0\1\7\3\0"+ + "\14\7\21\0\1\6\4\0\1\6\2\0\12\6\1\0\1\6\3\0"+ + "\5\6\6\0\1\6\1\0\1\6\1\0\1\6\1\0\4\6\1\0"+ + "\13\6\2\0\4\6\5\0\5\6\4\0\1\6\21\0\51\6\u0a77\0"+ + "\57\6\1\0\57\6\1\0\205\6\6\0\4\6\3\7\2\6\14\0"+ + "\46\6\1\0\1\6\5\0\1\6\2\0\70\6\7\0\1\6\17\0"+ + "\1\7\27\6\11\0\7\6\1\0\7\6\1\0\7\6\1\0\7\6"+ + "\1\0\7\6\1\0\7\6\1\0\7\6\1\0\7\6\1\0\40\7"+ + "\57\0\1\6\u01d5\0\3\6\31\0\11\6\6\7\1\0\5\6\2\0"+ + "\5\6\4\0\126\6\2\0\2\7\2\0\3\6\1\0\132\6\1\0"+ + "\4\6\5\0\51\6\3\0\136\6\21\0\33\6\65\0\20\6\u0200\0"+ + "\u19b6\6\112\0\u51cd\6\63\0\u048d\6\103\0\56\6\2\0\u010d\6\3\0"+ + "\20\6\12\7\2\6\24\0\57\6\1\7\4\0\12\7\1\0\31\6"+ + "\7\0\1\7\120\6\2\7\45\0\11\6\2\0\147\6\2\0\4\6"+ + "\1\0\4\6\14\0\13\6\115\0\12\6\1\7\3\6\1\7\4\6"+ + "\1\7\27\6\5\7\20\0\1\6\7\0\64\6\14\0\2\7\62\6"+ + "\21\7\13\0\12\7\6\0\22\7\6\6\3\0\1\6\4\0\12\7"+ + "\34\6\10\7\2\0\27\6\15\7\14\0\35\6\3\0\4\7\57\6"+ + "\16\7\16\0\1\6\12\7\46\0\51\6\16\7\11\0\3\6\1\7"+ + "\10\6\2\7\2\0\12\7\6\0\27\6\3\0\1\6\1\7\4\0"+ + "\60\6\1\7\1\6\3\7\2\6\2\7\5\6\2\7\1\6\1\7"+ + "\1\6\30\0\3\6\2\0\13\6\5\7\2\0\3\6\2\7\12\0"+ + "\6\6\2\0\6\6\2\0\6\6\11\0\7\6\1\0\7\6\221\0"+ + "\43\6\10\7\1\0\2\7\2\0\12\7\6\0\u2ba4\6\14\0\27\6"+ + "\4\0\61\6\u2104\0\u016e\6\2\0\152\6\46\0\7\6\14\0\5\6"+ + "\5\0\1\6\1\7\12\6\1\0\15\6\1\0\5\6\1\0\1\6"+ + "\1\0\2\6\1\0\2\6\1\0\154\6\41\0\u016b\6\22\0\100\6"+ + "\2\0\66\6\50\0\15\6\3\0\20\7\20\0\7\7\14\0\2\6"+ + "\30\0\3\6\31\0\1\6\6\0\5\6\1\0\207\6\2\0\1\7"+ + "\4\0\1\6\13\0\12\7\7\0\32\6\4\0\1\6\1\0\32\6"+ + "\13\0\131\6\3\0\6\6\2\0\6\6\2\0\6\6\2\0\3\6"+ + "\3\0\2\6\3\0\2\6\22\0\3\7\4\0\14\6\1\0\32\6"+ + "\1\0\23\6\1\0\2\6\1\0\17\6\2\0\16\6\42\0\173\6"+ + "\105\0\65\6\210\0\1\7\202\0\35\6\3\0\61\6\57\0\37\6"+ + "\21\0\33\6\65\0\36\6\2\0\44\6\4\0\10\6\1\0\5\6"+ + "\52\0\236\6\2\0\12\7\u0356\0\6\6\2\0\1\6\1\0\54\6"+ + "\1\0\2\6\3\0\1\6\2\0\27\6\252\0\26\6\12\0\32\6"+ + "\106\0\70\6\6\0\2\6\100\0\1\6\3\7\1\0\2\7\5\0"+ + "\4\7\4\6\1\0\3\6\1\0\33\6\4\0\3\7\4\0\1\7"+ + "\40\0\35\6\203\0\66\6\12\0\26\6\12\0\23\6\215\0\111\6"+ + "\u03b7\0\3\7\65\6\17\7\37\0\12\7\20\0\3\7\55\6\13\7"+ + "\2\0\1\7\22\0\31\6\7\0\12\7\6\0\3\7\44\6\16\7"+ + "\1\0\12\7\100\0\3\7\60\6\16\7\4\6\13\0\12\7\u04a6\0"+ + "\53\6\15\7\10\0\12\7\u0936\0\u036f\6\221\0\143\6\u0b9d\0\u042f\6"+ + "\u33d1\0\u0239\6\u04c7\0\105\6\13\0\1\6\56\7\20\0\4\7\15\6"+ + "\u4060\0\2\6\u2163\0\5\7\3\0\26\7\2\0\7\7\36\0\4\7"+ + "\224\0\3\7\u01bb\0\125\6\1\0\107\6\1\0\2\6\2\0\1\6"+ + "\2\0\2\6\2\0\4\6\1\0\14\6\1\0\1\6\1\0\7\6"+ + "\1\0\101\6\1\0\4\6\2\0\10\6\1\0\7\6\1\0\34\6"+ + "\1\0\4\6\1\0\5\6\1\0\1\6\3\0\7\6\1\0\u0154\6"+ + "\2\0\31\6\1\0\31\6\1\0\37\6\1\0\31\6\1\0\37\6"+ + "\1\0\31\6\1\0\37\6\1\0\31\6\1\0\37\6\1\0\31\6"+ + "\1\0\10\6\2\0\62\7\u1600\0\4\6\1\0\33\6\1\0\2\6"+ + "\1\0\1\6\2\0\1\6\1\0\12\6\1\0\4\6\1\0\1\6"+ + "\1\0\1\6\6\0\1\6\4\0\1\6\1\0\1\6\1\0\1\6"+ + "\1\0\3\6\1\0\2\6\1\0\1\6\2\0\1\6\1\0\1\6"+ + "\1\0\1\6\1\0\1\6\1\0\1\6\1\0\2\6\1\0\1\6"+ + "\2\0\4\6\1\0\7\6\1\0\4\6\1\0\4\6\1\0\1\6"+ + "\1\0\12\6\1\0\21\6\5\0\3\6\1\0\5\6\1\0\21\6"+ + "\u1144\0\ua6d7\6\51\0\u1035\6\13\0\336\6\u3fe2\0\u021e\6\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\u05ee\0"+ + "\1\7\36\0\140\7\200\0\360\7\uffff\0\uffff\0\ufe12\0"; + + /** + * Translates characters to character classes + */ + private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); + + /** + * Translates DFA states to action switch labels. + */ + private static final int [] ZZ_ACTION = zzUnpackAction(); + + private static final String ZZ_ACTION_PACKED_0 = + "\3\0\1\1\2\2\1\3\1\1\1\4\1\1\1\5"+ + "\1\1\1\5\1\1\4\4\1\6\1\7\1\10\1\11"+ + "\1\12\1\13\1\14\1\15\2\16\1\1\1\17\1\3"+ + "\1\0\1\20\1\21\1\22\1\0\2\21\1\22\2\0"+ + "\4\4\2\23\1\24\1\25\1\23\1\26\1\27\1\30"+ + "\1\31\1\32\1\33\1\23\2\3\2\0\2\34\1\21"+ + "\1\22\1\0\5\4\4\0\1\34\1\21\1\35\3\4"+ + "\1\36\1\37\1\0\1\40\1\34\1\21\2\4\1\41"+ + "\1\34\1\21\2\4\1\34\1\21\1\4\1\42\1\34"+ + "\1\21\1\4\1\34\1\21\1\43\1\34\7\21"; + + private static int [] zzUnpackAction() { + int [] result = new int[112]; + int offset = 0; + offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAction(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + + /** + * Translates a state to a row index in the transition table + */ + private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); + + private static final String ZZ_ROWMAP_PACKED_0 = + "\0\0\0\57\0\136\0\215\0\274\0\215\0\353\0\u011a"+ + "\0\u0149\0\u0178\0\u01a7\0\u01d6\0\u0205\0\u0234\0\u0263\0\u0292"+ + "\0\u02c1\0\u02f0\0\215\0\215\0\215\0\215\0\215\0\215"+ + "\0\215\0\u031f\0\u034e\0\215\0\u037d\0\215\0\u03ac\0\u03db"+ + "\0\u040a\0\u0439\0\u0468\0\u0497\0\u04c6\0\u04f5\0\u0524\0\u0553"+ + "\0\u0234\0\u0582\0\u05b1\0\u05e0\0\u060f\0\215\0\u063e\0\215"+ + "\0\215\0\u066d\0\215\0\215\0\215\0\215\0\215\0\215"+ + "\0\u069c\0\u06cb\0\215\0\u06fa\0\u0729\0\u0497\0\u0758\0\u0787"+ + "\0\u07b6\0\u07b6\0\u07e5\0\u0814\0\u0843\0\u0872\0\u08a1\0\u08d0"+ + "\0\u08ff\0\u092e\0\u095d\0\u098c\0\u09bb\0\u0149\0\u09ea\0\u0a19"+ + "\0\u0a48\0\u0149\0\215\0\u069c\0\215\0\u0a77\0\u0aa6\0\u0ad5"+ + "\0\u0b04\0\u0149\0\u0b33\0\u0b62\0\u0b91\0\u0bc0\0\u0bef\0\u0c1e"+ + "\0\u0c4d\0\u0149\0\u0c7c\0\u0cab\0\u0cda\0\u0d09\0\u0d38\0\u0149"+ + "\0\215\0\u0d67\0\u0d96\0\u0dc5\0\u0df4\0\u0e23\0\u0e52\0\u0468"; + + private static int [] zzUnpackRowMap() { + int [] result = new int[112]; + int offset = 0; + offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackRowMap(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int high = packed.charAt(i++) << 16; + result[j++] = high | packed.charAt(i++); + } + return j; + } + + /** + * The transition table of the DFA + */ + private static final int [] ZZ_TRANS = zzUnpackTrans(); + + private static final String ZZ_TRANS_PACKED_0 = + "\1\4\1\5\1\6\1\7\1\10\1\4\1\11\1\4"+ + "\1\12\1\13\1\14\1\15\2\11\2\15\1\16\1\11"+ + "\2\4\1\17\1\20\3\11\1\21\3\11\1\22\4\11"+ + "\1\23\1\24\1\25\1\26\1\27\1\30\1\31\1\11"+ + "\1\4\1\11\1\4\1\7\1\4\1\32\1\33\1\34"+ + "\20\32\1\35\24\32\1\36\6\32\57\4\61\0\1\6"+ + "\57\0\1\7\51\0\1\7\5\0\1\37\1\40\57\0"+ + "\2\11\1\0\1\11\1\0\5\11\1\0\1\11\2\0"+ + "\16\11\7\0\1\11\1\0\1\11\2\0\1\11\6\0"+ + "\1\41\5\0\2\41\3\0\1\41\2\0\16\41\7\0"+ + "\1\41\1\0\1\41\14\0\1\42\1\0\1\43\1\44"+ + "\1\0\1\45\1\46\1\47\1\50\6\0\1\50\22\0"+ + "\1\44\14\0\1\43\1\0\1\15\2\0\2\15\1\51"+ + "\47\0\1\15\1\0\1\15\2\0\2\15\1\47\1\50"+ + "\6\0\1\50\37\0\1\47\1\0\1\47\2\0\2\47"+ + "\45\0\2\11\1\0\1\11\1\0\5\11\1\0\1\11"+ + "\2\0\1\11\1\52\14\11\7\0\1\11\1\0\1\11"+ + "\2\0\1\11\6\0\2\11\1\0\1\11\1\0\5\11"+ + "\1\0\1\11\2\0\1\53\15\11\7\0\1\11\1\0"+ + "\1\11\2\0\1\11\6\0\2\11\1\0\1\11\1\0"+ + "\5\11\1\0\1\11\2\0\7\11\1\54\6\11\7\0"+ + "\1\11\1\0\1\11\2\0\1\11\6\0\2\11\1\0"+ + "\1\11\1\0\5\11\1\0\1\11\2\0\12\11\1\55"+ + "\3\11\7\0\1\11\1\0\1\11\2\0\1\11\1\32"+ + "\2\0\20\32\1\0\24\32\1\0\6\32\2\0\1\34"+ + "\54\0\1\56\2\0\6\56\1\57\4\56\2\57\3\56"+ + "\1\60\1\61\1\62\3\56\1\63\3\56\1\64\1\65"+ + "\11\56\1\66\1\67\1\70\1\71\3\0\1\37\1\72"+ + "\1\73\54\37\5\74\1\75\51\74\6\0\2\41\1\0"+ + "\1\41\1\0\5\41\1\0\1\41\2\0\16\41\7\0"+ + "\1\41\1\0\1\41\2\0\1\41\11\0\1\42\1\0"+ + "\1\43\2\0\1\45\1\46\1\47\1\50\6\0\1\50"+ + "\37\0\1\43\1\0\1\43\2\0\2\43\1\47\1\50"+ + "\6\0\1\50\37\0\1\76\1\0\1\77\1\0\3\77"+ + "\1\0\1\77\5\0\3\77\1\0\1\77\15\0\1\77"+ + "\16\0\1\46\1\0\1\43\2\0\2\46\1\47\1\50"+ + "\6\0\1\50\37\0\1\100\1\0\1\43\2\0\2\100"+ + "\1\47\1\50\6\0\1\50\37\0\1\47\1\0\1\47"+ + "\2\0\2\47\1\0\1\50\6\0\1\50\37\0\1\101"+ + "\1\102\1\101\2\0\2\101\2\0\1\102\42\0\2\11"+ + "\1\0\1\11\1\0\5\11\1\0\1\11\2\0\2\11"+ + "\1\103\13\11\7\0\1\11\1\0\1\11\2\0\1\11"+ + "\6\0\2\11\1\0\1\11\1\0\5\11\1\0\1\11"+ + "\2\0\3\11\1\104\7\11\1\105\2\11\7\0\1\11"+ + "\1\0\1\11\2\0\1\11\6\0\2\11\1\0\1\11"+ + "\1\0\5\11\1\0\1\11\2\0\2\11\1\106\13\11"+ + "\7\0\1\11\1\0\1\11\2\0\1\11\6\0\2\11"+ + "\1\0\1\11\1\0\5\11\1\0\1\11\2\0\1\11"+ + "\1\107\14\11\7\0\1\11\1\0\1\11\2\0\1\11"+ + "\11\0\1\110\4\0\2\110\50\0\1\111\1\0\1\111"+ + "\1\0\3\111\1\0\1\111\5\0\3\111\1\0\1\111"+ + "\15\0\1\111\16\0\1\112\1\0\1\112\1\0\3\112"+ + "\1\0\1\112\5\0\3\112\1\0\1\112\15\0\1\112"+ + "\7\0\1\73\54\0\5\74\1\113\51\74\4\0\1\73"+ + "\1\75\62\0\1\114\1\0\1\114\1\0\3\114\1\0"+ + "\1\114\5\0\3\114\1\0\1\114\15\0\1\114\16\0"+ + "\1\115\1\0\1\43\2\0\2\115\1\47\1\50\6\0"+ + "\1\50\37\0\1\101\1\0\1\101\2\0\2\101\45\0"+ + "\2\11\1\0\1\11\1\0\5\11\1\0\1\11\2\0"+ + "\2\11\1\116\13\11\7\0\1\11\1\0\1\11\2\0"+ + "\1\11\6\0\2\11\1\0\1\11\1\0\5\11\1\0"+ + "\1\11\2\0\4\11\1\117\11\11\7\0\1\11\1\0"+ + "\1\11\2\0\1\11\6\0\2\11\1\0\1\11\1\0"+ + "\5\11\1\0\1\11\2\0\1\120\15\11\7\0\1\11"+ + "\1\0\1\11\2\0\1\11\6\0\2\11\1\0\1\11"+ + "\1\0\5\11\1\0\1\11\2\0\10\11\1\121\5\11"+ + "\7\0\1\11\1\0\1\11\2\0\1\11\6\0\2\11"+ + "\1\0\1\11\1\0\5\11\1\0\1\11\2\0\4\11"+ + "\1\122\11\11\7\0\1\11\1\0\1\11\2\0\1\11"+ + "\11\0\1\123\4\0\2\123\50\0\1\124\1\0\1\124"+ + "\1\0\3\124\1\0\1\124\5\0\3\124\1\0\1\124"+ + "\15\0\1\124\16\0\1\125\1\0\1\125\1\0\3\125"+ + "\1\0\1\125\5\0\3\125\1\0\1\125\15\0\1\125"+ + "\5\0\4\74\1\73\1\113\51\74\11\0\1\126\1\0"+ + "\1\126\1\0\3\126\1\0\1\126\5\0\3\126\1\0"+ + "\1\126\15\0\1\126\16\0\1\127\1\0\1\43\2\0"+ + "\2\127\1\47\1\50\6\0\1\50\34\0\2\11\1\0"+ + "\1\11\1\0\5\11\1\0\1\11\2\0\5\11\1\130"+ + "\10\11\7\0\1\11\1\0\1\11\2\0\1\11\6\0"+ + "\2\11\1\0\1\11\1\0\5\11\1\0\1\11\2\0"+ + "\14\11\1\131\1\11\7\0\1\11\1\0\1\11\2\0"+ + "\1\11\6\0\2\11\1\0\1\11\1\0\5\11\1\0"+ + "\1\11\2\0\4\11\1\132\11\11\7\0\1\11\1\0"+ + "\1\11\2\0\1\11\11\0\1\133\1\0\1\133\1\0"+ + "\3\133\1\0\1\133\5\0\3\133\1\0\1\133\15\0"+ + "\1\133\16\0\1\134\1\0\1\43\2\0\2\134\1\47"+ + "\1\50\6\0\1\50\34\0\2\11\1\0\1\11\1\0"+ + "\5\11\1\0\1\11\2\0\6\11\1\135\7\11\7\0"+ + "\1\11\1\0\1\11\2\0\1\11\6\0\2\11\1\0"+ + "\1\11\1\0\5\11\1\0\1\11\2\0\15\11\1\136"+ + "\7\0\1\11\1\0\1\11\2\0\1\11\11\0\1\137"+ + "\1\0\1\137\1\0\3\137\1\0\1\137\5\0\3\137"+ + "\1\0\1\137\15\0\1\137\16\0\1\140\1\0\1\43"+ + "\2\0\2\140\1\47\1\50\6\0\1\50\34\0\2\11"+ + "\1\0\1\11\1\0\5\11\1\0\1\11\2\0\1\141"+ + "\15\11\7\0\1\11\1\0\1\11\2\0\1\11\6\0"+ + "\2\11\1\0\1\11\1\0\5\11\1\0\1\11\2\0"+ + "\1\142\15\11\7\0\1\11\1\0\1\11\2\0\1\11"+ + "\11\0\1\143\1\0\1\143\1\0\3\143\1\0\1\143"+ + "\5\0\3\143\1\0\1\143\15\0\1\143\16\0\1\144"+ + "\1\0\1\43\2\0\2\144\1\47\1\50\6\0\1\50"+ + "\34\0\2\11\1\0\1\11\1\0\5\11\1\0\1\11"+ + "\2\0\4\11\1\145\11\11\7\0\1\11\1\0\1\11"+ + "\2\0\1\11\11\0\1\146\1\0\1\146\1\0\3\146"+ + "\1\0\1\146\5\0\3\146\1\0\1\146\15\0\1\146"+ + "\16\0\1\147\1\0\1\43\2\0\2\147\1\47\1\50"+ + "\6\0\1\50\34\0\2\11\1\0\1\11\1\0\5\11"+ + "\1\0\1\11\2\0\3\11\1\150\12\11\7\0\1\11"+ + "\1\0\1\11\2\0\1\11\11\0\1\151\1\0\1\151"+ + "\1\0\3\151\1\0\1\151\5\0\3\151\1\0\1\151"+ + "\15\0\1\151\16\0\1\152\1\0\1\43\2\0\2\152"+ + "\1\47\1\50\6\0\1\50\37\0\1\153\1\0\1\43"+ + "\2\0\2\153\1\47\1\50\6\0\1\50\37\0\1\154"+ + "\1\0\1\43\2\0\2\154\1\47\1\50\6\0\1\50"+ + "\37\0\1\155\1\0\1\43\2\0\2\155\1\47\1\50"+ + "\6\0\1\50\37\0\1\156\1\0\1\43\2\0\2\156"+ + "\1\47\1\50\6\0\1\50\37\0\1\157\1\0\1\43"+ + "\2\0\2\157\1\47\1\50\6\0\1\50\37\0\1\160"+ + "\1\0\1\43\2\0\2\160\1\47\1\50\6\0\1\50"+ + "\26\0"; + + private static int [] zzUnpackTrans() { + int [] result = new int[3713]; + int offset = 0; + offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackTrans(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + value--; + do result[j++] = value; while (--count > 0); + } + return j; + } + + + /* error codes */ + private static final int ZZ_UNKNOWN_ERROR = 0; + private static final int ZZ_NO_MATCH = 1; + private static final int ZZ_PUSHBACK_2BIG = 2; + + /* error messages for the codes above */ + private static final String ZZ_ERROR_MSG[] = { + "Unkown internal scanner error", + "Error: could not match input", + "Error: pushback value was too large" + }; + + /** + * ZZ_ATTRIBUTE[aState] contains the attributes of state aState + */ + private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); + + private static final String ZZ_ATTRIBUTE_PACKED_0 = + "\3\0\1\11\1\1\1\11\14\1\7\11\2\1\1\11"+ + "\1\1\1\11\1\1\1\0\3\1\1\0\3\1\2\0"+ + "\4\1\1\11\1\1\2\11\1\1\6\11\2\1\1\11"+ + "\2\0\4\1\1\0\5\1\4\0\7\1\1\11\1\0"+ + "\1\11\23\1\1\11\7\1"; + + private static int [] zzUnpackAttribute() { + int [] result = new int[112]; + int offset = 0; + offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAttribute(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + /** the input device */ + private java.io.Reader zzReader; + + /** the current state of the DFA */ + private int zzState; + + /** the current lexical state */ + private int zzLexicalState = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; + + /** the textposition at the last accepting state */ + private int zzMarkedPos; + + /** the current text position in the buffer */ + private int zzCurrentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int zzStartRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int zzEndRead; + + /** number of newlines encountered up to the start of the matched text */ + private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + private int yycolumn; + + /** + * zzAtBOL == true <=> the scanner is currently at the beginning of a line + */ + private boolean zzAtBOL = true; + + /** zzAtEOF == true <=> the scanner is at the EOF */ + private boolean zzAtEOF; + + /** denotes if the user-EOF-code has already been executed */ + private boolean zzEOFDone; + + /** + * The number of occupied positions in zzBuffer beyond zzEndRead. + * When a lead/high surrogate has been read from the input stream + * into the final zzBuffer position, this will have a value of 1; + * otherwise, it will have a value of 0. + */ + private int zzFinalHighSurrogate = 0; + + /* user code: */ + + StringBuilder string = new StringBuilder(); + + private static String xmlTagName = ""; + + public int yychar() { + return yychar; + } + + private final Stack pushedBack = new Stack<>(); + + public int yyline() { + return yyline + 1; + } + + public void pushback(ParsedSymbol symb) { + pushedBack.push(symb); + last = null; + } + + ParsedSymbol last; + public ParsedSymbol lex() throws java.io.IOException, Amf3ParseException{ + ParsedSymbol ret = null; + if (!pushedBack.isEmpty()){ + ret = last = pushedBack.pop(); + } else { + ret = last = yylex(); + } + return ret; + } + + + + /** + * Creates a new scanner + * + * @param in the java.io.Reader to read input from. + */ + public Amf3Lexer(java.io.Reader in) { + this.zzReader = in; + } + + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char [] zzUnpackCMap(String packed) { + char [] map = new char[0x110000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 2856) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do map[j++] = value; while (--count > 0); + } + return map; + } + + + /** + * Refills the input buffer. + * + * @return false, iff there was new input. + * + * @exception java.io.IOException if any I/O-Error occurs + */ + private boolean zzRefill() throws java.io.IOException { + + /* first: make room (if you can) */ + if (zzStartRead > 0) { + zzEndRead += zzFinalHighSurrogate; + zzFinalHighSurrogate = 0; + System.arraycopy(zzBuffer, zzStartRead, + zzBuffer, 0, + zzEndRead-zzStartRead); + + /* translate stored positions */ + zzEndRead-= zzStartRead; + zzCurrentPos-= zzStartRead; + zzMarkedPos-= zzStartRead; + zzStartRead = 0; + } + + /* is the buffer big enough? */ + if (zzCurrentPos >= zzBuffer.length - zzFinalHighSurrogate) { + /* if not: blow it up */ + char newBuffer[] = new char[zzBuffer.length*2]; + System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); + zzBuffer = newBuffer; + zzEndRead += zzFinalHighSurrogate; + zzFinalHighSurrogate = 0; + } + + /* fill the buffer with new input */ + int requested = zzBuffer.length - zzEndRead; + int totalRead = 0; + while (totalRead < requested) { + int numRead = zzReader.read(zzBuffer, zzEndRead + totalRead, requested - totalRead); + if (numRead == -1) { + break; + } + totalRead += numRead; + } + + if (totalRead > 0) { + zzEndRead += totalRead; + if (totalRead == requested) { /* possibly more input available */ + if (Character.isHighSurrogate(zzBuffer[zzEndRead - 1])) { + --zzEndRead; + zzFinalHighSurrogate = 1; + } + } + return false; + } + + // totalRead = 0: End of stream + return true; + } + + + /** + * Closes the input stream. + */ + public final void yyclose() throws java.io.IOException { + zzAtEOF = true; /* indicate end of file */ + zzEndRead = zzStartRead; /* invalidate buffer */ + + if (zzReader != null) + zzReader.close(); + } + + + /** + * Resets the scanner to read from a new input stream. + * Does not close the old reader. + * + * All internal variables are reset, the old input stream + * cannot be reused (internal buffer is discarded and lost). + * Lexical state is set to ZZ_INITIAL. + * + * Internal scan buffer is resized down to its initial length, if it has grown. + * + * @param reader the new input stream + */ + public final void yyreset(java.io.Reader reader) { + zzReader = reader; + zzAtBOL = true; + zzAtEOF = false; + zzEOFDone = false; + zzEndRead = zzStartRead = 0; + zzCurrentPos = zzMarkedPos = 0; + zzFinalHighSurrogate = 0; + yyline = yychar = yycolumn = 0; + zzLexicalState = YYINITIAL; + if (zzBuffer.length > ZZ_BUFFERSIZE) + zzBuffer = new char[ZZ_BUFFERSIZE]; + } + + + /** + * Returns the current lexical state. + */ + public final int yystate() { + return zzLexicalState; + } + + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + public final void yybegin(int newState) { + zzLexicalState = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + public final String yytext() { + return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead ); + } + + + /** + * Returns the character at position pos from the + * matched text. + * + * It is equivalent to yytext().charAt(pos), but faster + * + * @param pos the position of the character to fetch. + * A value from 0 to yylength()-1. + * + * @return the character at position pos + */ + public final char yycharat(int pos) { + return zzBuffer[zzStartRead+pos]; + } + + + /** + * Returns the length of the matched text region. + */ + public final int yylength() { + return zzMarkedPos-zzStartRead; + } + + + /** + * Reports an error that occured while scanning. + * + * In a wellformed scanner (no or only correct usage of + * yypushback(int) and a match-all fallback rule) this method + * will only be called with things that "Can't Possibly Happen". + * If this method is called, something is seriously wrong + * (e.g. a JFlex bug producing a faulty scanner etc.). + * + * Usual syntax/scanner level error handling should be done + * in error fallback rules. + * + * @param errorCode the code of the errormessage to display + */ + private void zzScanError(int errorCode) { + String message; + try { + message = ZZ_ERROR_MSG[errorCode]; + } + catch (ArrayIndexOutOfBoundsException e) { + message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; + } + + throw new Error(message); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + public void yypushback(int number) { + if ( number > yylength() ) + zzScanError(ZZ_PUSHBACK_2BIG); + + zzMarkedPos -= number; + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception java.io.IOException if any I/O-Error occurs + */ + public ParsedSymbol yylex() throws java.io.IOException, Amf3ParseException { + int zzInput; + int zzAction; + + // cached fields: + int zzCurrentPosL; + int zzMarkedPosL; + int zzEndReadL = zzEndRead; + char [] zzBufferL = zzBuffer; + char [] zzCMapL = ZZ_CMAP; + + int [] zzTransL = ZZ_TRANS; + int [] zzRowMapL = ZZ_ROWMAP; + int [] zzAttrL = ZZ_ATTRIBUTE; + + while (true) { + zzMarkedPosL = zzMarkedPos; + + yychar+= zzMarkedPosL-zzStartRead; + + zzAction = -1; + + zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; + + zzState = ZZ_LEXSTATE[zzLexicalState]; + + // set up zzAction for empty match case: + int zzAttributes = zzAttrL[zzState]; + if ( (zzAttributes & 1) == 1 ) { + zzAction = zzState; + } + + + zzForAction: { + while (true) { + + if (zzCurrentPosL < zzEndReadL) { + zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL, zzEndReadL); + zzCurrentPosL += Character.charCount(zzInput); + } + else if (zzAtEOF) { + zzInput = YYEOF; + break zzForAction; + } + else { + // store back cached positions + zzCurrentPos = zzCurrentPosL; + zzMarkedPos = zzMarkedPosL; + boolean eof = zzRefill(); + // get translated positions and possibly new buffer + zzCurrentPosL = zzCurrentPos; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + zzEndReadL = zzEndRead; + if (eof) { + zzInput = YYEOF; + break zzForAction; + } + else { + zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL, zzEndReadL); + zzCurrentPosL += Character.charCount(zzInput); + } + } + int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ]; + if (zzNext == -1) break zzForAction; + zzState = zzNext; + + zzAttributes = zzAttrL[zzState]; + if ( (zzAttributes & 1) == 1 ) { + zzAction = zzState; + zzMarkedPosL = zzCurrentPosL; + if ( (zzAttributes & 8) == 8 ) break zzForAction; + } + + } + } + + // store back cached position + zzMarkedPos = zzMarkedPosL; + + switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { + case 1: + { + } + case 36: break; + case 2: + { yyline++; + } + case 37: break; + case 3: + { /*ignore*/ + } + case 38: break; + case 4: + { return new ParsedSymbol(SymbolGroup.IDENTIFIER, SymbolType.IDENTIFIER, yytext()); + } + case 39: break; + case 5: + { return new ParsedSymbol(SymbolGroup.INTEGER, SymbolType.INTEGER, Long.parseLong((yytext()))); + } + case 40: break; + case 6: + { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.CURLY_OPEN, yytext()); + } + case 41: break; + case 7: + { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.CURLY_CLOSE, yytext()); + } + case 42: break; + case 8: + { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.BRACKET_OPEN, yytext()); + } + case 43: break; + case 9: + { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.BRACKET_CLOSE, yytext()); + } + case 44: break; + case 10: + { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.COMMA, yytext()); + } + case 45: break; + case 11: + { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.COLON, yytext()); + } + case 46: break; + case 12: + { string.setLength(0); + yybegin(STRING); + } + case 47: break; + case 13: + { string.append(yytext()); + } + case 48: break; + case 14: + { yybegin(YYINITIAL); yyline++; + } + case 49: break; + case 15: + { yybegin(YYINITIAL); + // length also includes the trailing quote + return new ParsedSymbol(SymbolGroup.STRING, SymbolType.STRING, string.toString()); + } + case 50: break; + case 16: + { return new ParsedSymbol(SymbolGroup.IDENTIFIER, SymbolType.REFERENCE, yytext().substring(1)); + } + case 51: break; + case 17: + { return new ParsedSymbol(SymbolGroup.INTEGER, SymbolType.INTEGER, Long.parseLong(yytext(), 8)); + } + case 52: break; + case 18: + { return new ParsedSymbol(SymbolGroup.DOUBLE, SymbolType.DOUBLE, Double.parseDouble((yytext()))); + } + case 53: break; + case 19: + { string.append('\\'); /*illegal escape sequence*/ + } + case 54: break; + case 20: + { string.append('\\'); + } + case 55: break; + case 21: + { string.append('\n'); + } + case 56: break; + case 22: + { string.append('\f'); + } + case 57: break; + case 23: + { string.append('\t'); + } + case 58: break; + case 24: + { string.append('\r'); + } + case 59: break; + case 25: + { string.append('\"'); + } + case 60: break; + case 26: + { string.append('\b'); + } + case 61: break; + case 27: + { string.append('\''); + } + case 62: break; + case 28: + { return new ParsedSymbol(SymbolGroup.INTEGER, SymbolType.INTEGER, Long.parseLong(yytext().substring(2), 16)); + } + case 63: break; + case 29: + { return new ParsedSymbol(SymbolGroup.GLOBALCONST, SymbolType.NULL, yytext()); + } + case 64: break; + case 30: + { return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.TRUE, yytext()); + } + case 65: break; + case 31: + { char val = (char) Integer.parseInt(yytext().substring(1), 8); + string.append(val); + } + case 66: break; + case 32: + { char val = (char) Integer.parseInt(yytext().substring(2), 16); + string.append(val); + } + case 67: break; + case 33: + { return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.FALSE, yytext()); + } + case 68: break; + case 34: + { return new ParsedSymbol(SymbolGroup.UNKNOWN, SymbolType.UNKNOWN, yytext()); + } + case 69: break; + case 35: + { return new ParsedSymbol(SymbolGroup.GLOBALCONST, SymbolType.UNDEFINED, yytext()); + } + case 70: break; + default: + if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { + zzAtEOF = true; + { + return new ParsedSymbol(SymbolGroup.EOF, SymbolType.EOF, null); + } + } + else { + zzScanError(ZZ_NO_MATCH); + } + } + } + } + + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3ParseException.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3ParseException.java new file mode 100644 index 000000000..0f343f283 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3ParseException.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.amf.amf3; + +import com.jpexs.decompiler.flash.ParseException; + +/** + * + * @author JPEXS + */ +public class Amf3ParseException extends ParseException { + + public Amf3ParseException(String text, long line) { + super(text, line); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/ParsedSymbol.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/ParsedSymbol.java new file mode 100644 index 000000000..c2244cbf6 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/ParsedSymbol.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.amf.amf3; + +/** + * + * @author JPEXS + */ +public class ParsedSymbol { + + public SymbolGroup group; + + public Object value; + + public SymbolType type; + + public ParsedSymbol(SymbolGroup group, SymbolType type) { + this.group = group; + this.type = type; + this.value = null; + } + + public ParsedSymbol(SymbolGroup group, SymbolType type, Object value) { + this.group = group; + this.type = type; + this.value = value; + } + + @Override + public String toString() { + return group.toString() + " " + type.toString() + " " + (value != null ? value.toString() : ""); + } + + public boolean isType(Object... types) { + for (Object t : types) { + if (t instanceof SymbolGroup) { + if (group == t) { + return true; + } + } + if (t instanceof SymbolType) { + if (type == t) { + return true; + } + } + } + return false; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolGroup.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolGroup.java new file mode 100644 index 000000000..a6a8b953f --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolGroup.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.amf.amf3; + +/** + * + * @author JPEXS + */ +public enum SymbolGroup { + OPERATOR, + KEYWORD, + STRING, + COMMENT, + IDENTIFIER, + INTEGER, + DOUBLE, + EOF, + GLOBALCONST, + UNKNOWN +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolType.java new file mode 100644 index 000000000..03fad7666 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolType.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.amf.amf3; + +/** + * + * @author JPEXS + */ +public enum SymbolType { + NULL, + UNDEFINED, + FALSE, + TRUE, + CURLY_OPEN, + CURLY_CLOSE, + BRACKET_OPEN, + BRACKET_CLOSE, + COMMA, + COLON, + UNKNOWN, + REFERENCE, + IDENTIFIER, + INTEGER, + STRING, + DOUBLE, + EOF +} diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java index af5abf607..49ecef29f 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java @@ -63,18 +63,6 @@ public class Amf3OutputStreamTest { }; } - private static byte[] readFile(File file) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte buf[] = new byte[1024]; - int cnt; - try (FileInputStream fis = new FileInputStream(file)) { - while ((cnt = fis.read(buf)) > 0) { - baos.write(buf, 0, cnt); - } - } - return baos.toByteArray(); - } - @Test(dataProvider = "files") public void testRecompile(String fileName) throws FileNotFoundException, IOException, NoSerializerExistsException { diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/importers/amf/amf3/parser/Amf3ImporterTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/importers/amf/amf3/parser/Amf3ImporterTest.java new file mode 100644 index 000000000..5ac5fae8b --- /dev/null +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/importers/amf/amf3/parser/Amf3ImporterTest.java @@ -0,0 +1,85 @@ +package com.jpexs.decompiler.flash.importers.amf.amf3.parser; + +import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3ParseException; +import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3Importer; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; +import com.jpexs.decompiler.flash.amf.amf3.Amf3InputStream; +import com.jpexs.decompiler.flash.amf.amf3.NoSerializerExistsException; +import com.jpexs.decompiler.flash.amf.amf3.ObjectTypeSerializeHandler; +import com.jpexs.decompiler.flash.amf.amf3.Pair; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.MemoryInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class Amf3ImporterTest { + + @DataProvider(name = "files") + public static Object[][] provideSamples() { + return new Object[][]{ + {"all.bin"} + }; + } + + private Map getSerializers() { + Map serializers = new HashMap<>(); + serializers.put("CustomClass", new ObjectTypeSerializeHandler() { + @Override + public List> readObject(String className, InputStream is) throws IOException { + List> members = new ArrayList<>(); + members.add(new Pair<>("val8", (long) is.read())); + members.add(new Pair<>("val32", (long) ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read())))); + return members; + } + + @Override + public void writeObject(List> members, OutputStream os) throws IOException { + Map memberMap = new HashMap<>(); + for (Pair m : members) { + memberMap.put(m.getFirst(), m.getSecond()); + } + os.write((int) (long) (Long) memberMap.get("val8")); + long val32 = (long) (Long) memberMap.get("val32"); + os.write((int) ((val32 >> 24) & 0xff)); + os.write((int) ((val32 >> 16) & 0xff)); + os.write((int) ((val32 >> 8) & 0xff)); + os.write((int) (val32 & 0xff)); + } + }); + return serializers; + } + + @Test(dataProvider = "files") + public void testRecompile(String fileName) throws IOException, NoSerializerExistsException, Amf3ParseException { + + String originalFile = "testdata/amf3/generated/" + fileName; + + Amf3InputStream is = new Amf3InputStream(new MemoryInputStream(Helper.readFile(originalFile))); + + Object val = is.readValue("testValue", getSerializers()); + String exported1File = "testdata/amf3/generated/exported1." + fileName; + String exported2File = "testdata/amf3/generated/exported2." + fileName; + + String exported1 = Amf3Exporter.amfToString(val); + Helper.writeFile(exported1File, exported1.getBytes("UTF-8")); + Amf3Importer imp = new Amf3Importer(); + Object valImported = imp.stringToAmf(exported1); + String exported2 = Amf3Exporter.amfToString(valImported); + + Helper.writeFile(exported2File, exported2.getBytes("UTF-8")); + + Assert.assertEquals(exported2, exported1); + new File(exported1File).delete(); + new File(exported2File).delete(); + + } +} diff --git a/libsrc/ffdec_lib/testdata/amf3/AmfTest.as b/libsrc/ffdec_lib/testdata/amf3/AmfTest.as index 37c7934fc..83658a2c7 100644 --- a/libsrc/ffdec_lib/testdata/amf3/AmfTest.as +++ b/libsrc/ffdec_lib/testdata/amf3/AmfTest.as @@ -164,6 +164,9 @@ package var objWithCustom:ObjectWithCustom = new ObjectWithCustom("1","2",custom,"4","5","6"); writeToFile("noserializer_object_sealed.bin", objWithCustom); + + + writeToFile("simple.bin", {"a":7, "b":29}); } } } \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/amf3/AmfTest.swf b/libsrc/ffdec_lib/testdata/amf3/AmfTest.swf index eed907c1861886a822e576ff7dfffc76e21f47fb..b7547e8c043e2ec9b6d85a77a402dcce92cbb41b 100644 GIT binary patch delta 2033 zcmVYu}mjr@1byoEqWhwkRtfMZJXn zYAe#g*mu?1q6KA!6dqR)fqo)L|G;#3D1z$fN%bT3=csH;SQ|NHIXbqw-t#jVZm=i= zy$4(N>_^3c)QA@9ZJ4h+%>bg%k5L{NBE(dBeDg0kQlHG4V8=egZL@=O~vYICV1dSAEO|9Dvnu)YH(#j3TF8v}w+ziI(AfbD@_= zOI0QVX2Zu?)I6oVt?+h~^H^3E-v+;%S>o=OzNvvqbN9r z0F?0XKx_AUPpSxjiJq>-NX9rJvwziOCp%0PwWwpLSd#-Ev=Z7pRg*RTq*BBnHD0wa zpCqU>VGjn}4_^;s_0Q32wM?P%6Wu6ADt+K_Jb-j!^>Q@k41qD032cQRrFQ!Cg$7^v zIwU_?!RiUq@O6>)`AH`V2F6-=`Ai|}XcCF4n@d9PHN3}J4X3q$NLEIs(|<<5pGy24 z@m>v$@g<`KE%>-|in8_r>{%zro;QNeC4G1%aESa(l-FZtdX=p7r*TaSu(?gM<79?wjf z=v(A?SxHR5v^69PboR$XMhdO9(5|50C~m}#hab=|dns4BkCgO+`$7>z#8a1sJSKSc zaMspLeKz{gY@Ccyn?lf6v&J0rMna+Q;O|zi8B$9F5=-6ne*(A2e1G?mRrl=mS!d@{ z*_w>Z%CNF2$_UXS?jYCqFRZNAErqK=#5zZ_XTVnYu*<$;So&ZrI#0dHMlmyUGfuty zOZfRFY%JLAH&<>(amyxfTtdUo#+>a2ch~<_rSN8{WKzd&h0-tIg#76>l8YpUm@fEm zZTRV4Ew-Fz73<39GJk>s2bo@3QK)d!!P46P)sPrcdaaN>Fz+uxh-g@R^nHi|U@s`> zx!M!#2v%>O`)3&Z`Q0r4i{OF+ER$`Fl{m({#jKUgFC6Az1L5sj-f!td-sNG$F;}}B zz8L(GPXpPBQ4pH6=Bveo7KQ|VBDkpBL;kY!QguQ(^Z!^n6MtSxBvyD82f%F$xap#J zbEA3|0SPj)bYd#yB;(qFCR9=}U8zM3apUYi5&FJR^vF+Bzk;d9BvWqakZ)8U)ym0E zu8bzY|0Ku8!0{^3(zwVwbE56 zZ?a%6n&ROw0e{L)JHMbeWnI4@5B?Y$p4C*v*t_3u$)E+==e8bj+PB_Y`F)|4CEIDU@czQb&o&7yG2AG?dZ-x zfh?TzMea3uvH7V)4s_wj>+I+Emul?VC_giP+Fb7gxjWRMWo3zS75kfTB7_ihcn8P^ zj>`{VFndJ#qVBYvW8YSkJn(d`L&S08%X!fQj zL-w%#l}n7r;Hf{Vze=spcK#0XVo91q)y>wqT7NWlf|cb{mfsSt+f)s7THH7M@-z`X=!*Ot;1cuHJL|P;&JBHy%am$aqnyI2#}5rksM*j0pkSdIJ(@0eU=A z6X7beuIhzsWFF1qB0Au7_g@ zcVn*2^9N`cNtVugAK-SjamTVp!HJjNb!1();_bg(5c|6EtuNQv%q1F!ebnQZG-LD$ z6_&ALU}1@|>-c1T|M}#a#iNj-e_jEntU0DspixN;mq@tizq92efuL?fffXa~IXdb{ zGwmMdv-GU>6M39cZfdSWTml#}OR>qs^Bhy=k46Lmquz0^2=DPOlJJ<^8 z6bmevwHJ?vowHMJJkVNIyH^$+ss7D}1yC^(U|@0XqC>9YPhfgpHXPKwCGx^8;YezK zG3wL;ajXJz=6@k}F;@b}N9s>lrdWM=RDK8J)H;3wIZCUjk+K<#DAIC4ZphLg;(rZj zW5nXdyoif+B+ldMAi!~gn1O4YJfE^&6*Zg+3|gD#LEAgu)PeK_LCPfu18lMa=;}gI z&FY7$8j&KuG4nyomaz*re=BK_YdzOO6KLu0L6SKLMX-~P1Cci=w0Eh{oVu7pI(7zC zW7cfj91G9a+CbdS*>7~ZDIU~bN`FUOm{FXYa7XcSL$1QuQ8qeJ#NJ86gB0m!Y+DR#EGbvEfVk{c%-?04{1#PRdul@F)kEd) z^jE%JR^*c0x;`&*Q8Ebc)rlDjkWTd4++-@;p+hE?WQra#d=JENPHc#f-uRNLL5x&U zHQOG44L({;&z%ucW^;WdpMM}HY9t^L;!s~55fz{t1?jLuKTk%LKW+uFp$}fLOQqJ& zg7P>{N$d&QF4I8`R9u!ihqz+q$$NGSUbGw z8nU8(?FkwBHp|xCNPK+7=Liw@$6Ay1{bXA*!0sN!xm883$G@|`;yQDRuQ)4zrQ_GI zNZX=)XYa6Ph|PIhH&wcBhXbu@GV%2elOhfInXc?Ijw=kPH&AqTzc8o}J|2{JpVr*O zAB7%}HPi-Ff)Em%eSe2#HvS7W%z;Gy)IFytI)Z9>ra{j^GYHW&;Soo++L`HyH^6>d zKQ@9!S2P8bt-o1DZUE_RWhOZZiRoqLNK>761rf4G(Z&qj)3arOEMg;RlgxvLRK^%V zW1C)z5f^frR1PfalE?5VulAL&J6iD#mUQ2J9RJgMnC%rtBR_J!;WA0Gb6n)>RU!m(Y_*OyTQ$&i%>qRy z6mR4mhQP_CyMn!a5Ni@!L=kCB841l!)I*N~=6vgyYh`3t2xDY60*DWiFMest%xw`^ zs9$wTJH~!}tbe?g+iAcI*rrcKB9g2E^Ih}2E^sEh#_m-HC3J8eTwrv0kXAax=Ds5A zc*~H?_;)#biJL-r-~c3V6{u{J>c=mbh>OV;VM&K0dFSJFn?FjP;Mnf4u}X(Fm)^GB9n@3 znq{2;yjN_8R%u?oG0=w{8cQ|krJX9s^QOj@)%Zjg>ok3a0yvHDp&+;1~~!CvA9ao29~SA4SWrEIBtrky0C z@y}xsIZO~}PG^yKGL@i+XVms4>cXMV)cX@gASMbMq~i6`^YSH zOMjs(lf1PXgbw5EC*v?5?XznjlCZGK`JVyZ{}4Da(3rmtJBv6*G(|QZxj6FHmINR* zP_6Z!;R_+dTB}^(@A?Ju6G3?vHAgDk@(^(E<>DvO}~)`Hor|iAPKUs z1&CzKxP$N&Pf|+8EP;ueEIyFVv5j`?HF^T!%T?USRgnck0JLPBPhPOO_3Fd2IDb4U z&;elzd9|-pj!p`cIifR*Bp(7+*@&|04Rg$1Q1eTWUK^-*HDnv2DWJ z5yV0l^bU>WB{-Gg*b!f2su>Hz_*^3ecKX$1eChI6vkUm$LIzrTbvF%`|C{p&;v%KV ANdN!< diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/all.bin b/libsrc/ffdec_lib/testdata/amf3/generated/all.bin index 22b5dc4c008098fca024f38cc36163af09c22079..e9f33e746c12fe6e02b8c0fd65d92f680074aad8 100644 GIT binary patch literal 765 zcmYjPUvJYe5KkH>U6XZLX#rIgYo~&?mua@szfx6oV-Fx9fy8+B+{B$CvP4RgZQZBq z2Vh@#LJQUk8v?rZU_q+4&?(DN2y^Dt=Weal8{47boeT=h%ipIs}+TeP`^|XzK zD&Z>0=Mi%&+WM_0W!5X~div=Hm*?44;kVFWm$TGFedY5K)rz*+Y<`Op-2N0Lj`3ip z50PWlV4o~#v}C0w9FRN?*dmNsptU?KQy=f5Ao0s+vPhEYWKPp5rIT}EC+7lcq8^#A zGIo)sbhTf;xP0%z9?8<28OOLUDYjwzrHB5pIz&?1osMMvz@jKAi2p;O@>M15$-7ik zs+4uhpq4SW4F5Im$v{9ewx?g_H)qekI$GC*1seKUn8Y+))lAKDg70jFEcco%v(GUr zd|dd~4@nhygiq}3w{S83GE2kw)~bG$whld_5O{*elE3)VB7Rubcw3K_v9gS(HpC=m zw=@gy*;yE8fd9gu7D9wB6Q8IlD!d&GEMX8Zb)bV1Fi!Ajhgxj|s3E9s9aJGAg3+vF zB>ELUwCBPhiSsDZTNsjHJM{|}VItq=@Esg)TMFcsnd?^zDRnp^3Ysa%@jP#1gGGLpA&%FXr%Jgb2Rk7!0+Vc literal 765 zcmYjP-EPw`6m}ZNrOCQdb^Me-+o_=S2F-T*D@9c|b^#I+NQ`UCZQLm$OQbX%D_1-L zx9~W90C))=f*UT1<5+Y@a*mGueU8s}KHD*>n6NO;pmj`#EEP+3BNkbbzKOGH1?+aa zKLCIunSaaNJEjALnQt<2mZp4jP(C|?+WX_Sfs1vM-b`AGgmac{AQmVkK zEDhr;RQ;gx3p`=5xZ+uOBVGo{dL9XD`*5kEK?%FPj)G)SCjK%>rv99#Q_lUOZq;M zI^Qn?!|od9#=M0`(6%kugDv^>A#LIzTgQP|hOr1hn~XKDXeuY7F&ZNCi|Z9#>pl+J zWTc@Ns@YlvY@d57X8j^>r*D7C_%*vI>IWWC!pSQfrgX479VNwU5k*O%#{aEQ*M&ST z8S3AClrA}}NMisycZpmrxq5XxU`rmYM5*iHV*Y#S4x414>AA;BZA|yWk^({LwZBc$ uXM2yq6FO1X6xP&wJZ@l*>A`nS==cso%kZR1ynNWs(=Hv|IabeZ8UFx?Amuaw diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/custom.bin b/libsrc/ffdec_lib/testdata/amf3/generated/custom.bin index c3bed6b07..74ae73be5 100644 --- a/libsrc/ffdec_lib/testdata/amf3/generated/custom.bin +++ b/libsrc/ffdec_lib/testdata/amf3/generated/custom.bin @@ -1,3 +1,3 @@ - member3member1member2 -CustomClassÿÿö< \ No newline at end of file + member3member2 +CustomClassÿÿö<member1 \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_dictionary_key.bin b/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_dictionary_key.bin index 24d9b65ac5dc1a4d7c6463662f0261260e75e4aa..28445e990a36d0e51c174ee88df0b8fb0e654f5e 100644 GIT binary patch delta 45 zcmZ>9nINOV#+<^&9gR=2SLLxBSu~05Dw+ ANB{r; delta 45 zcmZ>9nINOV#V+n#T3nK!>ztEVTwMSE|2G>p?vRY4)KoU+6gECK=2SLLxBSu~09T6+ ANB{r; diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/simple.bin b/libsrc/ffdec_lib/testdata/amf3/generated/simple.bin new file mode 100644 index 000000000..ab4fd477c --- /dev/null +++ b/libsrc/ffdec_lib/testdata/amf3/generated/simple.bin @@ -0,0 +1,2 @@ + + ba \ No newline at end of file From 1c9e485dca0a6c11f97f60b14d68f0a70cb21d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Wed, 20 Jul 2016 22:16:45 +0200 Subject: [PATCH 17/20] AMF3 refactorings AMF3 editor in GUI for PlaceObject4 --- .../decompiler/flash/SWFInputStream.java | 7 +- .../decompiler/flash/SWFOutputStream.java | 7 +- .../decompiler/flash/amf/amf3/Amf3Value.java | 48 ++++++ .../amf/amf3/types/AbstractVectorType.java | 2 +- .../flash/amf/amf3/types/Amf3ValueType.java | 5 + .../flash/amf/amf3/types/ArrayType.java | 2 +- .../flash/amf/amf3/types/BasicType.java | 2 +- .../flash/amf/amf3/types/DateType.java | 2 +- .../flash/amf/amf3/types/DictionaryType.java | 2 +- .../flash/amf/amf3/types/ObjectType.java | 2 +- .../flash/amf/amf3/types/XmlDocType.java | 2 +- .../flash/amf/amf3/types/XmlType.java | 2 +- .../flash/tags/PlaceObject4Tag.java | 6 +- .../decompiler/flash/gui/GenericTagPanel.java | 7 + .../flash/gui/GenericTagTreePanel.java | 10 ++ .../generictageditors/Amf3ValueEditor.java | 144 ++++++++++++++++++ .../generictageditors/BinaryDataEditor.java | 5 + .../gui/generictageditors/BooleanEditor.java | 5 + .../gui/generictageditors/ColorEditor.java | 5 + .../generictageditors/GenericTagEditor.java | 2 + .../gui/generictageditors/NumberEditor.java | 5 + .../generictageditors/ScrollPanedEditor.java | 74 +++++++++ .../gui/generictageditors/StringEditor.java | 5 + 23 files changed, 334 insertions(+), 17 deletions(-) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Value.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/Amf3ValueType.java create mode 100644 src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java create mode 100644 src/com/jpexs/decompiler/flash/gui/generictageditors/ScrollPanedEditor.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java index eccdbad62..7649ff95b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java @@ -119,6 +119,7 @@ import com.jpexs.decompiler.flash.action.swf7.ActionImplementsOp; import com.jpexs.decompiler.flash.action.swf7.ActionThrow; import com.jpexs.decompiler.flash.action.swf7.ActionTry; import com.jpexs.decompiler.flash.amf.amf3.Amf3InputStream; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; import com.jpexs.decompiler.flash.amf.amf3.NoSerializerExistsException; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.dumpview.DumpInfo; @@ -767,13 +768,13 @@ public class SWFInputStream implements AutoCloseable { * @return * @throws IOException */ - public Object readAmf3Object(String name) throws IOException { + public Amf3Value readAmf3Object(String name) throws IOException { Amf3InputStream ai = new Amf3InputStream(is); ai.dumpInfo = this.dumpInfo; try { - return ai.readValue("amfData"); + return new Amf3Value(ai.readValue("amfData")); } catch (NoSerializerExistsException nse) { - return nse.getIncompleteData(); + return new Amf3Value(nse.getIncompleteData()); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java index 41296c298..9ad13c603 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFOutputStream.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash; import com.jpexs.decompiler.flash.amf.amf3.Amf3OutputStream; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; import com.jpexs.decompiler.flash.amf.amf3.NoSerializerExistsException; import com.jpexs.decompiler.flash.amf.amf3.ObjectTypeSerializeHandler; import com.jpexs.decompiler.flash.configuration.Configuration; @@ -1962,9 +1963,9 @@ public class SWFOutputStream extends OutputStream { * @throws IOException * @throws NoSerializerExistsException */ - public void writeAmf3Object(Object value, Map serializers) throws IOException, NoSerializerExistsException { + public void writeAmf3Object(Amf3Value value, Map serializers) throws IOException, NoSerializerExistsException { Amf3OutputStream ao = new Amf3OutputStream(os); - ao.writeValue(value, serializers); + ao.writeValue(value.getValue(), serializers); } /** @@ -1975,7 +1976,7 @@ public class SWFOutputStream extends OutputStream { * @throws IOException * @throws NoSerializerExistsException */ - public void writeAmf3Object(Object value) throws IOException, NoSerializerExistsException { + public void writeAmf3Object(Amf3Value value) throws IOException, NoSerializerExistsException { writeAmf3Object(value, new HashMap<>()); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Value.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Value.java new file mode 100644 index 000000000..45d4a7b04 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Value.java @@ -0,0 +1,48 @@ +package com.jpexs.decompiler.flash.amf.amf3; + +import com.jpexs.decompiler.flash.amf.amf3.types.Amf3ValueType; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; + +public class Amf3Value { + + private Object value; + + public Amf3Value(Object value) { + setValue(value); + } + + public void setValue(Object value) { + if (!isValueValid(value)) { + throw new IllegalArgumentException("Invalid Amf value: " + value.getClass().getSimpleName()); + } + this.value = value; + } + + public static boolean isValueValid(Object value) { + if (value instanceof Long) { + return true; + } + if (value instanceof Double) { + return true; + } + if (value instanceof String) { + return true; + } + if (value instanceof Boolean) { + return true; + } + if (value instanceof Amf3ValueType) { + return true; + } + return false; + } + + public Object getValue() { + return value; + } + + @Override + public String toString() { + return Amf3Exporter.amfToString(value); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java index 8e87f4009..9449e7049 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java @@ -5,7 +5,7 @@ import java.util.ArrayList; import java.util.List; import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; -public abstract class AbstractVectorType implements WithSubValues { +public abstract class AbstractVectorType implements WithSubValues, Amf3ValueType { private boolean fixed; private List values; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/Amf3ValueType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/Amf3ValueType.java new file mode 100644 index 000000000..9cbd90d01 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/Amf3ValueType.java @@ -0,0 +1,5 @@ +package com.jpexs.decompiler.flash.amf.amf3.types; + +public interface Amf3ValueType { + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java index 411d90003..637219def 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java @@ -7,7 +7,7 @@ import java.util.ArrayList; import java.util.List; import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; -public class ArrayType implements WithSubValues { +public class ArrayType implements WithSubValues, Amf3ValueType { private List denseValues; private List> associativeValues; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/BasicType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/BasicType.java index 1d5752451..6fc6866c9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/BasicType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/BasicType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -public enum BasicType { +public enum BasicType implements Amf3ValueType { NULL { @Override public String toString() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java index 50e73f215..e481a3cc9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java @@ -3,7 +3,7 @@ package com.jpexs.decompiler.flash.amf.amf3.types; import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; import java.util.Date; -public class DateType { +public class DateType implements Amf3ValueType { private double val; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java index bcae93132..5acd6a9dd 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java @@ -6,7 +6,7 @@ import java.util.ArrayList; import java.util.List; import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; -public class DictionaryType implements WithSubValues { +public class DictionaryType implements WithSubValues, Amf3ValueType { private boolean weakKeys; private List> pairs; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java index 4333a0f9c..98cf21353 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java @@ -8,7 +8,7 @@ import java.util.ArrayList; import java.util.List; import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; -public class ObjectType implements WithSubValues { +public class ObjectType implements WithSubValues, Amf3ValueType { private List> sealedMembers; private List> dynamicMembers; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlDocType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlDocType.java index 37207bccf..20fddf116 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlDocType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlDocType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -public class XmlDocType { +public class XmlDocType implements Amf3ValueType { private String data; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlType.java index bd0e77027..43beffc45 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/XmlType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -public class XmlType { +public class XmlType implements Amf3ValueType { private String data; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java index f078ef36b..5aed2b36b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.EndOfStreamException; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; import com.jpexs.decompiler.flash.amf.amf3.NoSerializerExistsException; import com.jpexs.decompiler.flash.tags.base.ASMSourceContainer; import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; @@ -234,8 +235,7 @@ public class PlaceObject4Tag extends PlaceObjectTypeTag implements ASMSourceCont @Reserved public boolean reserved; - //public byte[] amfData; //TODO: Parse AMF data? - public Object amfData; + public Amf3Value amfData; /** * Constructor @@ -246,7 +246,7 @@ public class PlaceObject4Tag extends PlaceObjectTypeTag implements ASMSourceCont super(swf, ID, NAME, null); } - public PlaceObject4Tag(SWF swf, boolean placeFlagMove, int depth, String className, int characterId, MATRIX matrix, CXFORMWITHALPHA colorTransform, int ratio, String name, int clipDepth, List surfaceFilterList, int blendMode, int bitmapCache, int visible, RGBA backgroundColor, CLIPACTIONS clipActions, Object amfData) { + public PlaceObject4Tag(SWF swf, boolean placeFlagMove, int depth, String className, int characterId, MATRIX matrix, CXFORMWITHALPHA colorTransform, int ratio, String name, int clipDepth, List surfaceFilterList, int blendMode, int bitmapCache, int visible, RGBA backgroundColor, CLIPACTIONS clipActions, Amf3Value amfData) { super(swf, ID, NAME, null); this.placeFlagHasClassName = className != null; this.placeFlagHasFilterList = surfaceFilterList != null; diff --git a/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java b/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java index 67e2522f0..efa2c213a 100644 --- a/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/GenericTagPanel.java @@ -17,12 +17,15 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; +import com.jpexs.decompiler.flash.gui.generictageditors.Amf3ValueEditor; import com.jpexs.decompiler.flash.gui.generictageditors.BinaryDataEditor; import com.jpexs.decompiler.flash.gui.generictageditors.BooleanEditor; import com.jpexs.decompiler.flash.gui.generictageditors.ChangeListener; import com.jpexs.decompiler.flash.gui.generictageditors.ColorEditor; import com.jpexs.decompiler.flash.gui.generictageditors.GenericTagEditor; import com.jpexs.decompiler.flash.gui.generictageditors.NumberEditor; +import com.jpexs.decompiler.flash.gui.generictageditors.ScrollPanedEditor; import com.jpexs.decompiler.flash.gui.generictageditors.StringEditor; import com.jpexs.decompiler.flash.gui.helpers.SpringUtilities; import com.jpexs.decompiler.flash.tags.Tag; @@ -409,6 +412,8 @@ public class GenericTagPanel extends JPanel implements ChangeListener { editor = new ColorEditor(name, obj, field, index, type); } else if (type.equals(ByteArrayRange.class)) { editor = new BinaryDataEditor(mainPanel, name, obj, field, index, type); + } else if (type.equals(Amf3Value.class)) { + editor = new ScrollPanedEditor(new Amf3ValueEditor(name, obj, field, index, type)); } else { if (value == null) { if (readonly) { @@ -436,6 +441,8 @@ public class GenericTagPanel extends JPanel implements ChangeListener { fieldPaths.put(name, parList); fieldIndices.put(name, parIndices); addRow(name, editor, field); + + ce.added(); } return 1; } diff --git a/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java b/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java index 6f27fc481..96eb27d64 100644 --- a/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java @@ -17,11 +17,14 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; +import com.jpexs.decompiler.flash.gui.generictageditors.Amf3ValueEditor; import com.jpexs.decompiler.flash.gui.generictageditors.BinaryDataEditor; import com.jpexs.decompiler.flash.gui.generictageditors.BooleanEditor; import com.jpexs.decompiler.flash.gui.generictageditors.ColorEditor; import com.jpexs.decompiler.flash.gui.generictageditors.GenericTagEditor; import com.jpexs.decompiler.flash.gui.generictageditors.NumberEditor; +import com.jpexs.decompiler.flash.gui.generictageditors.ScrollPanedEditor; import com.jpexs.decompiler.flash.gui.generictageditors.StringEditor; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.ASMSource; @@ -166,6 +169,8 @@ public class GenericTagTreePanel extends GenericTagPanel { editor = new ColorEditor(field.getName(), obj, field, index, type); } else if (type.equals(byte[].class) || type.equals(ByteArrayRange.class)) { editor = new BinaryDataEditor(mainPanel, field.getName(), obj, field, index, type); + } else if (type.equals(Amf3Value.class)) { + editor = new ScrollPanedEditor(new Amf3ValueEditor(field.getName(), obj, field, index, type)); } if (editor != null) { if (editors == null) { @@ -199,6 +204,9 @@ public class GenericTagTreePanel extends GenericTagPanel { nameLabel.setSize(nameLabel.getWidth(), editorComponent.getHeight()); editorComponent.setAlignmentY(TOP_ALIGNMENT); pan.add(editorComponent); + if (editorComponent instanceof GenericTagEditor) { + ((GenericTagEditor) editorComponent).added(); + } pan.setPreferredSize(new Dimension((int) nameLabel.getPreferredSize().getWidth() + 5 + (int) editorComponent.getPreferredSize().getWidth(), (int) editorComponent.getPreferredSize().getHeight())); } else { pan.setPreferredSize(new Dimension((int) nameLabel.getPreferredSize().getWidth(), (int) nameLabel.getPreferredSize().getHeight())); @@ -946,6 +954,8 @@ public class GenericTagTreePanel extends GenericTagPanel { return true; } else if (isByteArray || type.equals(ByteArrayRange.class)) { return true; + } else if (type.equals(Amf3Value.class)) { + return true; } else { return false; } diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java new file mode 100644 index 000000000..373dca6c2 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2016 Jindra + * + * 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 . + */ +package com.jpexs.decompiler.flash.gui.generictageditors; + +import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; +import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3Importer; +import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3ParseException; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.ReflectionTools; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.io.IOException; +import java.lang.reflect.Field; +import javax.swing.JEditorPane; + +/** + * + * @author JPEXS + */ +public class Amf3ValueEditor extends JEditorPane implements GenericTagEditor { + + private final Object obj; + + private final Field field; + + private final int index; + + private final Class type; + + private String fieldName; + + @Override + public void added() { + String s = getText(); + setContentType("text/javascript"); + setText(s); + } + + @Override + public boolean getScrollableTracksViewportWidth() { + return true; + } + + /* @Override + public Dimension getPreferredSize() { + Dimension ret = super.getPreferredSize(); + ret.width = 300; + return ret; + }*/ + @Override + public Component.BaselineResizeBehavior getBaselineResizeBehavior() { + return Component.BaselineResizeBehavior.CONSTANT_ASCENT; + } + + @Override + public int getBaseline(int width, int height) { + return 0; + } + + public Amf3ValueEditor(String fieldName, Object obj, Field field, int index, Class type) { + this.obj = obj; + this.field = field; + this.index = index; + this.type = type; + this.fieldName = fieldName; + try { + Amf3Value val = (Amf3Value) ReflectionTools.getValue(obj, field, index); + String stringVal = Amf3Exporter.amfToString(val.getValue()); + setText(stringVal); + } catch (IllegalArgumentException | IllegalAccessException ex) { + // ignore + } + } + + @Override + public void save() { + Amf3Importer importer = new Amf3Importer(); + try { + Object newValue = importer.stringToAmf(getText()); + ReflectionTools.setValue(obj, field, index, new Amf3Value(newValue)); + } catch (IOException | Amf3ParseException | IllegalArgumentException | IllegalAccessException ex) { + //ignore + } + } + + @Override + public void addChangeListener(final ChangeListener l) { + final GenericTagEditor t = this; + addFocusListener(new FocusAdapter() { + + @Override + public void focusLost(FocusEvent e) { + l.change(t); + } + + }); + } + + @Override + public Object getChangedValue() { + Amf3Importer importer = new Amf3Importer(); + try { + return new Amf3Value(importer.stringToAmf(getText())); + } catch (IOException | Amf3ParseException ex) { + //ignore + } + return null; + } + + @Override + public String getFieldName() { + return fieldName; + } + + @Override + public Field getField() { + return field; + } + + @Override + public String getReadOnlyValue() { + return Helper.escapeHTML(getChangedValue().toString()); + } + +} diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java index 030072b80..57f999c53 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/BinaryDataEditor.java @@ -139,4 +139,9 @@ public class BinaryDataEditor extends JButton implements GenericTagEditor { public int getBaseline(int width, int height) { return 0; } + + @Override + public void added() { + + } } diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java index 5f907548b..5474f3a2a 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/BooleanEditor.java @@ -38,6 +38,11 @@ public class BooleanEditor extends JCheckBox implements GenericTagEditor { private final String fieldName; + @Override + public void added() { + + } + public BooleanEditor(String fieldName, Object obj, Field field, int index, Class type) { super(); this.obj = obj; diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java index e01bc321e..8ac7fa53e 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/ColorEditor.java @@ -75,6 +75,11 @@ public class ColorEditor extends JPanel implements GenericTagEditor, ActionListe return colorType; } + @Override + public void added() { + + } + public ColorEditor(String fieldName, Object obj, Field field, int index, Class type) { this.obj = obj; this.field = field; diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java index d105e315a..ff55a7f5e 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/GenericTagEditor.java @@ -24,6 +24,8 @@ import java.lang.reflect.Field; */ public interface GenericTagEditor { + public void added(); + public void save(); public void addChangeListener(ChangeListener l); diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java index e79f46299..16b44fe92 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/NumberEditor.java @@ -57,6 +57,11 @@ public class NumberEditor extends JSpinner implements GenericTagEditor { return 0; } + @Override + public void added() { + + } + public NumberEditor(String fieldName, Object obj, Field field, int index, Class type, SWFType swfType) { setSize(100, getSize().height); setMaximumSize(getSize()); diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/ScrollPanedEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/ScrollPanedEditor.java new file mode 100644 index 000000000..af151a97c --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/ScrollPanedEditor.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 Jindra + * + * 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 . + */ +package com.jpexs.decompiler.flash.gui.generictageditors; + +import java.awt.Component; +import java.awt.Dimension; +import java.lang.reflect.Field; +import javax.swing.JScrollPane; + +/** + * + * @author JPEXS + */ +public class ScrollPanedEditor extends JScrollPane implements GenericTagEditor { + + private final GenericTagEditor editor; + + public ScrollPanedEditor(GenericTagEditor editor) { + super((Component) editor); + this.editor = editor; + Dimension d = new Dimension(500, 300); + setSize(d); + setPreferredSize(d); + } + + @Override + public void save() { + editor.save(); + } + + @Override + public void addChangeListener(ChangeListener l) { + editor.addChangeListener(l); + } + + @Override + public Object getChangedValue() { + return editor.getChangedValue(); + } + + @Override + public String getFieldName() { + return editor.getFieldName(); + } + + @Override + public Field getField() { + return editor.getField(); + } + + @Override + public String getReadOnlyValue() { + return editor.getReadOnlyValue(); + } + + @Override + public void added() { + editor.added(); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java index 4d7e8b218..c08cfb0cd 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/StringEditor.java @@ -126,4 +126,9 @@ public class StringEditor extends JTextArea implements GenericTagEditor { public String getReadOnlyValue() { return Helper.escapeHTML(getChangedValue().toString()); } + + @Override + public void added() { + + } } From 01c2079b07bfe26c3f7d399993a6c2929d62d094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Fri, 22 Jul 2016 21:35:48 +0200 Subject: [PATCH 18/20] AMF3 editor in GUI fixes Hex view fixes --- .../flash/amf/amf3/Amf3InputStream.java | 15 +++++++-------- .../decompiler/flash/amf/amf3/Amf3Value.java | 10 ++++++++++ .../decompiler/flash/tags/PlaceObject4Tag.java | 3 +-- .../gui/generictageditors/Amf3ValueEditor.java | 17 ++++++++++------- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java index c06785560..c13644348 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3InputStream.java @@ -34,11 +34,10 @@ public class Amf3InputStream extends InputStream { public final static Logger LOGGER = Logger.getLogger(Amf3InputStream.class.getName()); private final MemoryInputStream is; public DumpInfo dumpInfo; - private static String NO_REFERENCE_BIT_TEXT = "not reference"; - private static String REFERENCE_BIT_TEXT = "not reference"; - private static String OBJECT_INDEX_TEXT = "object index"; - private static String STRING_INDEX_TEXT = "string index"; - private static String TRAIT_INDEX_TEXT = "trait index"; + private static final String NO_REFERENCE_BIT_TEXT = "not reference"; + private static final String OBJECT_INDEX_TEXT = "object index"; + private static final String STRING_INDEX_TEXT = "string index"; + private static final String TRAIT_INDEX_TEXT = "trait index"; public Amf3InputStream(MemoryInputStream is) { this.is = is; @@ -146,11 +145,11 @@ public class Amf3InputStream extends InputStream { } private void renameU29O_ref() { - renameU29("U29O-ref", REFERENCE_BIT_TEXT, OBJECT_INDEX_TEXT); + renameU29("U29O-ref", NO_REFERENCE_BIT_TEXT, OBJECT_INDEX_TEXT); } private void renameU29S_ref() { - renameU29("U29S-ref", REFERENCE_BIT_TEXT, STRING_INDEX_TEXT); + renameU29("U29S-ref", NO_REFERENCE_BIT_TEXT, STRING_INDEX_TEXT); } private void renameU29Traits_ref() { @@ -493,7 +492,7 @@ public class Amf3InputStream extends InputStream { result = retObjectType; break markerswitch; } else { - renameU29("U29O-traits", NO_REFERENCE_BIT_TEXT, "not trait reference", "not externalized traits", "dynamic", "sealed count"); + renameU29("U29O-traits", NO_REFERENCE_BIT_TEXT, "not trait reference", "externalized traits", "dynamic", "sealed count"); int dynamicFlag = (int) ((objectU29 >> 3) & 1); int numSealed = (int) (objectU29 >> 4); LOGGER.log(Level.FINEST, "object dynamicFlag:{0}", dynamicFlag); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Value.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Value.java index 45d4a7b04..9da5c62a2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Value.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Value.java @@ -7,6 +7,10 @@ public class Amf3Value { private Object value; + public Amf3Value() { + setValue(null); + } + public Amf3Value(Object value) { setValue(value); } @@ -19,6 +23,9 @@ public class Amf3Value { } public static boolean isValueValid(Object value) { + if (value == null) { + return true; + } if (value instanceof Long) { return true; } @@ -43,6 +50,9 @@ public class Amf3Value { @Override public String toString() { + if (value == null) { + return ""; + } return Amf3Exporter.amfToString(value); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java index 5aed2b36b..f26ef451f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java @@ -430,8 +430,7 @@ public class PlaceObject4Tag extends PlaceObjectTypeTag implements ASMSourceCont if (placeFlagHasClipActions) { sos.writeCLIPACTIONS(clipActions); } - if (amfData != null) { - //sos.write(Helper.readFile("d:\\Dropbox\\Programovani\\JavaSE\\FFDec\\libsrc\\ffdec_lib\\testdata\\amf3\\generated\\all.bin")); + if (amfData != null && amfData.getValue() != null) { try { sos.writeAmf3Object(amfData); } catch (NoSerializerExistsException ex) { diff --git a/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java b/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java index 373dca6c2..222394eca 100644 --- a/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java +++ b/src/com/jpexs/decompiler/flash/gui/generictageditors/Amf3ValueEditor.java @@ -84,8 +84,12 @@ public class Amf3ValueEditor extends JEditorPane implements GenericTagEditor { this.fieldName = fieldName; try { Amf3Value val = (Amf3Value) ReflectionTools.getValue(obj, field, index); - String stringVal = Amf3Exporter.amfToString(val.getValue()); - setText(stringVal); + if (val == null || val.getValue() == null) { + setText(""); + } else { + String stringVal = Amf3Exporter.amfToString(val.getValue()); + setText(stringVal); + } } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore } @@ -93,11 +97,9 @@ public class Amf3ValueEditor extends JEditorPane implements GenericTagEditor { @Override public void save() { - Amf3Importer importer = new Amf3Importer(); try { - Object newValue = importer.stringToAmf(getText()); - ReflectionTools.setValue(obj, field, index, new Amf3Value(newValue)); - } catch (IOException | Amf3ParseException | IllegalArgumentException | IllegalAccessException ex) { + ReflectionTools.setValue(obj, field, index, getChangedValue()); + } catch (IllegalArgumentException | IllegalAccessException ex) { //ignore } } @@ -118,8 +120,9 @@ public class Amf3ValueEditor extends JEditorPane implements GenericTagEditor { @Override public Object getChangedValue() { Amf3Importer importer = new Amf3Importer(); + String textVal = getText(); try { - return new Amf3Value(importer.stringToAmf(getText())); + return textVal.trim().isEmpty() ? null : new Amf3Value(importer.stringToAmf(textVal)); } catch (IOException | Amf3ParseException ex) { //ignore } From d5b492609b6e2e36080a60bf357b64db1018127d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Fri, 22 Jul 2016 21:39:58 +0200 Subject: [PATCH 19/20] getAmfData method to PlaceObjectTypeTag --- .../com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java | 6 ++++++ .../com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java | 7 +++++++ .../com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java | 6 ++++++ .../com/jpexs/decompiler/flash/tags/PlaceObjectTag.java | 7 +++++++ .../decompiler/flash/tags/base/PlaceObjectTypeTag.java | 3 +++ 5 files changed, 29 insertions(+) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java index 7c61e56d6..a4604ced5 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.tags; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; import com.jpexs.decompiler.flash.tags.base.ASMSourceContainer; import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; import com.jpexs.decompiler.flash.types.BasicType; @@ -436,4 +437,9 @@ public class PlaceObject2Tag extends PlaceObjectTypeTag implements ASMSourceCont matrix = old; } } + + @Override + public Amf3Value getAmfData() { + return null; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java index ea1f877a5..dddf83f8b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.EndOfStreamException; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; import com.jpexs.decompiler.flash.tags.base.ASMSourceContainer; import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; import com.jpexs.decompiler.flash.types.BasicType; @@ -615,4 +616,10 @@ public class PlaceObject3Tag extends PlaceObjectTypeTag implements ASMSourceCont matrix = old; } } + + @Override + public Amf3Value getAmfData() { + return null; + } + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java index f26ef451f..311ac8011 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java @@ -633,4 +633,10 @@ public class PlaceObject4Tag extends PlaceObjectTypeTag implements ASMSourceCont matrix = old; } } + + @Override + public Amf3Value getAmfData() { + return amfData; + } + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObjectTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObjectTag.java index 63c18b004..c3a4c3d91 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObjectTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObjectTag.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.tags; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.CLIPACTIONS; @@ -255,4 +256,10 @@ public class PlaceObjectTag extends PlaceObjectTypeTag { matrix = old; } } + + @Override + public Amf3Value getAmfData() { + return null; + } + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/PlaceObjectTypeTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/PlaceObjectTypeTag.java index a5a566d18..9aa8603f2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/PlaceObjectTypeTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/PlaceObjectTypeTag.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.tags.base; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.types.CLIPACTIONS; import com.jpexs.decompiler.flash.types.ColorTransform; @@ -76,6 +77,8 @@ public abstract class PlaceObjectTypeTag extends Tag implements CharacterIdTag { public abstract void writeTagWithMatrix(SWFOutputStream sos, MATRIX m) throws IOException; + public abstract Amf3Value getAmfData(); + @Override public String getName() { String result = super.getName(); From ce54b87eca73182e9f719f8955fd9b190b5234c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Fri, 22 Jul 2016 23:15:39 +0200 Subject: [PATCH 20/20] Issue #1255 Export instance metadata of basic types (integer,string,double) to FLA --- .../decompiler/flash/xfl/XFLConverter.java | 132 ++++++++++++++++-- 1 file changed, 118 insertions(+), 14 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java index 640dd8f6e..dc7a5d623 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java @@ -23,6 +23,9 @@ import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFCompression; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; +import com.jpexs.decompiler.flash.amf.amf3.Pair; +import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.MovieExporter; import com.jpexs.decompiler.flash.exporters.SoundExporter; @@ -70,6 +73,7 @@ import com.jpexs.decompiler.flash.tags.base.SoundTag; import com.jpexs.decompiler.flash.tags.base.TextTag; import com.jpexs.decompiler.flash.tags.enums.ImageFormat; import com.jpexs.decompiler.flash.tags.font.CharacterRanges; +import com.jpexs.decompiler.flash.timeline.Timelined; import com.jpexs.decompiler.flash.types.BUTTONCONDACTION; import com.jpexs.decompiler.flash.types.BUTTONRECORD; import com.jpexs.decompiler.flash.types.CLIPACTIONRECORD; @@ -167,6 +171,10 @@ public class XFLConverter { public static final int KEY_MODE_SHAPE_LAYERS = 8192; + public static final String PUBLISH_DATA_PREFIX = "PUB_PRST_DATA"; + + public static final String PUBLISH_DATA_FORMAT = "_EMBED_SWF_"; + private final Random random = new Random(123); // predictable random private static void convertShapeEdge(MATRIX mat, SHAPERECORD record, int x, int y, StringBuilder ret) { @@ -581,7 +589,7 @@ public class XFLConverter { LINESTYLEARRAY actualLinestyles = lineStyles; int strokeStyleOrig = 0; - fillStyleCount = fillStyles.fillStyles.length; + fillStyleCount = fillStyles == null ? 0 : fillStyles.fillStyles.length; for (SHAPERECORD edge : shapeRecords) { if (edge instanceof StyleChangeRecord) { StyleChangeRecord scr = (StyleChangeRecord) edge; @@ -602,7 +610,7 @@ public class XFLConverter { if ((fillStyle0 <= 0) && (fillStyle1 <= 0) && (strokeStyle > 0) && morphshape) { if (shapeNum == 4) { if (strokeStyleOrig > 0) { - if (!((LINESTYLE2) actualLinestyles.lineStyles[strokeStyleOrig]).hasFillFlag) { + if (actualLinestyles != null && !((LINESTYLE2) actualLinestyles.lineStyles[strokeStyleOrig]).hasFillFlag) { RGBA color = (RGBA) actualLinestyles.lineStyles[strokeStyleOrig].color; if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0) { empty = true; @@ -701,7 +709,7 @@ public class XFLConverter { if ((fillStyle0 <= 0) && (fillStyle1 <= 0) && (strokeStyle > 0) && morphshape) { if (shapeNum == 4) { if (strokeStyleOrig > 0) { - if (!((LINESTYLE2) actualLinestyles.lineStyles[strokeStyleOrig]).hasFillFlag) { + if (actualLinestyles != null && !((LINESTYLE2) actualLinestyles.lineStyles[strokeStyleOrig]).hasFillFlag) { RGBA color = (RGBA) actualLinestyles.lineStyles[strokeStyleOrig].color; if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0) { empty = true; @@ -745,7 +753,7 @@ public class XFLConverter { if ((fillStyle0 <= 0) && (fillStyle1 <= 0) && (strokeStyle > 0) && morphshape) { if (shapeNum == 4) { if (strokeStyleOrig > 0) { - if (!((LINESTYLE2) actualLinestyles.lineStyles[strokeStyleOrig]).hasFillFlag) { + if (actualLinestyles != null && !((LINESTYLE2) actualLinestyles.lineStyles[strokeStyleOrig]).hasFillFlag) { RGBA color = (RGBA) actualLinestyles.lineStyles[strokeStyleOrig].color; if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0) { empty = true; @@ -1058,7 +1066,7 @@ public class XFLConverter { } } - private static void convertSymbolInstance(String name, MATRIX matrix, ColorTransform colorTransform, boolean cacheAsBitmap, int blendMode, List filters, boolean isVisible, RGBA backgroundColor, CLIPACTIONS clipActions, CharacterTag tag, HashMap characters, ReadOnlyTagList tags, FLAVersion flaVersion, XFLXmlWriter writer) throws XMLStreamException { + private static void convertSymbolInstance(String name, MATRIX matrix, ColorTransform colorTransform, boolean cacheAsBitmap, int blendMode, List filters, boolean isVisible, RGBA backgroundColor, CLIPACTIONS clipActions, Amf3Value metadata, CharacterTag tag, HashMap characters, ReadOnlyTagList tags, FLAVersion flaVersion, XFLXmlWriter writer) throws XMLStreamException { if (matrix == null) { matrix = new MATRIX(); } @@ -1185,6 +1193,55 @@ public class XFLConverter { writer.writeEndElement(); writer.writeEndElement(); } + if (metadata != null && (metadata.getValue() instanceof ObjectType)) { + ObjectType metadataObject = (ObjectType) metadata.getValue(); + + if (metadataObject.isDynamic()) { + writer.writeStartElement("persistentData"); + List exportedNames = new ArrayList<>(); + for (Pair dynamicMember : metadataObject.getDynamicMembers()) { + String n = dynamicMember.getFirst(); + Object v = dynamicMember.getSecond(); + if (v instanceof Long) { + exportedNames.add(n); + writer.writeStartElement("PD"); + writer.writeAttribute("n", n); + writer.writeAttribute("t", "i"); + writer.writeAttribute("v", (Long) v); + writer.writeEndElement(); + exportedNames.add(n); + } else if (v instanceof Double) { + writer.writeStartElement("PD"); + writer.writeAttribute("n", n); + writer.writeAttribute("t", "d"); + writer.writeAttribute("v", (Double) v); + writer.writeEndElement(); + exportedNames.add(n); + } else if (v instanceof String) { + writer.writeStartElement("PD"); + writer.writeAttribute("n", n); + //missing t attrinute = string (maybe "s"?) + writer.writeAttribute("v", (String) v); + writer.writeEndElement(); + exportedNames.add(n); + } + /* + From JSFL, also data types integerArray ("I"), doubleArray("D") and byteArray("B") can be set. + These datatypes can be in the FLA file but are not exported to SWF with _EMBED_SWF_ publish format. + */ + } + + //Mark these names for publishing in embedded swf format: (setPublishPersistentData function in JSFL) + for (String n : exportedNames) { + writer.writeStartElement("PD"); + writer.writeAttribute("n", PUBLISH_DATA_PREFIX + PUBLISH_DATA_FORMAT + n); + writer.writeAttribute("t", "i"); + writer.writeAttribute("v", 1); + writer.writeEndElement(); + } + writer.writeEndElement(); + } + } writer.writeEndElement(); } @@ -1332,7 +1389,7 @@ public class XFLConverter { } else if (character instanceof DefineVideoStreamTag) { convertVideoInstance(null, matrix, (DefineVideoStreamTag) character, null, recCharWriter); } else { - convertSymbolInstance(null, matrix, colorTransformAlpha, false, blendMode, filters, true, null, null, characters.get(rec.characterId), characters, tags, flaVersion, recCharWriter); + convertSymbolInstance(null, matrix, colorTransformAlpha, false, blendMode, filters, true, null, null, null, characters.get(rec.characterId), characters, tags, flaVersion, recCharWriter); } int duration = frame - lastFrame; @@ -1909,6 +1966,7 @@ public class XFLConverter { CharacterTag character = null; MATRIX matrix = null; + Amf3Value metadata = null; String instanceName = null; ColorTransform colorTransForm = null; boolean cacheAsBitmap = false; @@ -1953,6 +2011,10 @@ public class XFLConverter { if (characters.containsKey(characterId)) { character = characters.get(characterId); if (po.flagMove()) { + Amf3Value metadata2 = po.getAmfData(); + if (metadata2 != null && metadata2.getValue() != null) { + metadata = metadata2; + } MATRIX matrix2 = po.getMatrix(); if (matrix2 != null) { matrix = matrix2; @@ -1986,6 +2048,7 @@ public class XFLConverter { ratio = ratio2; } } else { + metadata = po.getAmfData(); matrix = po.getMatrix(); instanceName = po.getInstanceName(); colorTransForm = po.getColorTransform(); @@ -2009,6 +2072,7 @@ public class XFLConverter { shapeTween = false; } character = null; + metadata = null; matrix = null; instanceName = null; colorTransForm = null; @@ -2041,7 +2105,7 @@ public class XFLConverter { } else if (character instanceof DefineVideoStreamTag) { convertVideoInstance(instanceName, matrix, (DefineVideoStreamTag) character, clipActions, elementsWriter); } else { - convertSymbolInstance(instanceName, matrix, colorTransForm, cacheAsBitmap, blendMode, filters, isVisible, backGroundColor, clipActions, character, characters, tags, flaVersion, elementsWriter); + convertSymbolInstance(instanceName, matrix, colorTransForm, cacheAsBitmap, blendMode, filters, isVisible, backGroundColor, clipActions, metadata, character, characters, tags, flaVersion, elementsWriter); } } } @@ -2601,14 +2665,14 @@ public class XFLConverter { writer.writeEndElement(); writer.writeStartElement("textRuns"); - int fontId = -1; + int fontId; FontTag font = null; - String fontName = null; + String fontName; String psFontName = null; int textHeight = -1; RGB textColor = null; RGBA textColorA = null; - boolean newline = false; + boolean newline; boolean firstRun = true; @SuppressWarnings("unchecked") List leftMargins = (List) attrs.get("allLeftMargins"); @@ -2776,8 +2840,8 @@ public class XFLConverter { int indent = -1; int lineSpacing = -1; String alignment = null; - boolean italic = false; - boolean bold = false; + boolean italic; + boolean bold; String fontFace = null; int size = -1; RGBA textColor = null; @@ -2807,9 +2871,9 @@ public class XFLConverter { bold = ft.isBold(); size = det.fontHeight; fontFace = fontName; - String installedFont = null; + String installedFont; if ((installedFont = FontTag.isFontFamilyInstalled(fontName)) != null) { - fontName = installedFont; + //fontName = installedFont; fontFace = new Font(installedFont, (italic ? Font.ITALIC : 0) | (bold ? Font.BOLD : 0) | (!italic && !bold ? Font.PLAIN : 0), size < 0 ? 10 : size).getPSName(); } @@ -2866,6 +2930,33 @@ public class XFLConverter { } } + private boolean hasAmfMetadata(Tag tag) { + if (tag instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag po = (PlaceObjectTypeTag) tag; + if (po.getAmfData() != null && po.getAmfData().getValue() != null) { + return true; + } + } + if (tag instanceof Timelined) { + Timelined tl = (Timelined) tag; + for (Tag t : tl.getTags()) { + if (hasAmfMetadata(t)) { + return true; + } + } + } + return false; + } + + private boolean hasAmfMetadata(SWF swf) { + for (Tag t : swf.getTags()) { + if (hasAmfMetadata(t)) { + return true; + } + } + return false; + } + public void convertSWF(AbortRetryIgnoreHandler handler, SWF swf, String swfFileName, String outfile, XFLExportSettings settings, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion flaVersion) throws IOException, InterruptedException { FileAttributesTag fa = swf.getFileAttributes(); @@ -2895,6 +2986,7 @@ public class XFLConverter { List nonLibraryShapes = getNonLibraryShapes(swf.getTags(), characters); Map characterClasses = getCharacterClasses(swf.getTags()); Map characterVariables = getCharacterVariables(swf.getTags()); + boolean hasAmfMetadata = hasAmfMetadata(swf); String backgroundColor = "#ffffff"; SetBackgroundColorTag setBgColorTag = swf.getBackgroundColor(); @@ -2941,6 +3033,18 @@ public class XFLConverter { convertTimeline(0, nonLibraryShapes, backgroundColor, swf.getTags(), swf.getTags(), characters, "Scene 1", flaVersion, files, domDocument); domDocument.writeEndElement(); + if (hasAmfMetadata) { + domDocument.writeStartElement("persistentData"); + + domDocument.writeStartElement("PD"); + domDocument.writeAttribute("n", PUBLISH_DATA_PREFIX + PUBLISH_DATA_FORMAT); + domDocument.writeAttribute("t", "i"); + domDocument.writeAttribute("v", 1); + domDocument.writeEndElement(); + + domDocument.writeEndElement(); + } + domDocument.writeEndElement(); } catch (XMLStreamException ex) { logger.log(Level.SEVERE, null, ex);