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