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(); + } }