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.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 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;
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 MemoryInputStream is;
public DumpInfo dumpInfo;
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;
}
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 void endDumpLevel() {
endDumpLevel(null);
}
public void endDumpLevel(Object value) {
if (dumpInfo != null) {
dumpInfo.lengthBytes = is.getPos() - dumpInfo.startByte;
dumpInfo.previewValue = value;
dumpInfo = dumpInfo.parent;
}
}
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) readInternal();
}
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(String name) throws IOException {
newDumpLevel(name, "DOUBLE");
long lval = readLong();
double ret = Double.longBitsToDouble(lval);
endDumpLevel(EcmaScript.toString(ret));
return ret;
}
public long readU29(String name) throws IOException {
newDumpLevel(name, "U29");
long val = readU29Internal();
endDumpLevel(val);
return val;
}
private void renameU29O_ref() {
renameU29("U29O-ref", NO_REFERENCE_BIT_TEXT, OBJECT_INDEX_TEXT);
}
private void renameU29S_ref() {
renameU29("U29S-ref", NO_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<String> 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);
endDumpLevel(val);
return val;
}
public long readU29Internal() throws IOException {
long val = 0;
for (int i = 1; i <= 4; i++) {
int b = readInternal();
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 = size == 32 ? 0xFFFFFFFF : (1 << size) - 1; // 111111...up to size
long positiveVal = (~(val - 1)) & mask;
long negativeVal = -positiveVal;
return negativeVal;
}
return val;
}
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 EndOfStreamException();
}
String retString = new String(buf, "UTF-8");
endDumpLevel("\"" + Helper.escapeActionScriptString(retString) + "\"");
return retString;
}
public String readUtf8Vr(String name, List<String> stringTable) throws IOException {
newDumpLevel(name, "UTF-8-vr");
long u = readU29("U29S");
int stringNoRefFlag = (int) (u & 1);
String retString;
if (stringNoRefFlag == 1) {
renameU29("U29S-value", NO_REFERENCE_BIT_TEXT, "byte length");
long byteLength = u >> 1; //TODO: long strings, int is not enough for them
retString = readUtf8Char("characters", byteLength);
if (byteLength > 0) {
stringTable.add(retString);
}
LOGGER.log(Level.FINE, "Read string: \"{0}\"", retString);
} else { //flag==0
renameU29S_ref();
int stringRefTableIndex = (int) (u >> 1);
retString = stringTable.get(stringRefTableIndex);
LOGGER.log(Level.FINE, "Read string: reference({0}):" + retString, stringRefTableIndex);
}
endDumpLevel("\"" + Helper.escapeActionScriptString(retString) + "\"");
return retString;
}
private int readInternal() throws IOException {
int ret = read();
if (ret == -1) {
throw new EndOfStreamException();
}
return ret;
}
@Override
public int read() throws IOException {
return is.read();
}
public Object readValue(String name) throws IOException, NoSerializerExistsException {
return readValue(name, new HashMap<>());
}
public Object readValue(String name, Map<String, ObjectTypeSerializeHandler> serializers) throws IOException, NoSerializerExistsException {
return readValue(name, serializers, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
}
private Object readValue(String name, Map<String, ObjectTypeSerializeHandler> serializers,
List<Object> objectTable,
List<Traits> traitsTable,
List<String> stringTable
) throws IOException, NoSerializerExistsException {
newDumpLevel(name, "value-type");
Object result;
try {
int marker = readU8("marker");
markerswitch:
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);
result = ival;
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);
result = dval;
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);
result = sval;
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) {
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);
XmlDocType retXmlDoc = new XmlDocType(xval);
objectTable.add(retXmlDoc);
result = retXmlDoc;
} else {
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?
}
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) {
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);
LOGGER.log(Level.FINER, "Date value: {0}", retDate);
objectTable.add(retDate);
result = retDate;
} else {
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?
}
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) {
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});
Map<String, Object> assocPart = new ListMap<>();
List<Object> densePart = new ArrayList<>();
ArrayType retArray = new ArrayType();
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()) {
renameLastDump("UTF-8-empty");
break;
} else {
try {
Object val = readValue("value", serializers, objectTable, traitsTable, stringTable);
assocPart.put(key, val);
} catch (NoSerializerExistsException nse) {
assocPart.put(key, nse.getIncompleteData());
retArray.setAssociativeValues(assocPart);
throw new NoSerializerExistsException(nse.getClassName(), retArray, nse);
}
}
}
retArray.setAssociativeValues(assocPart);
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));
} catch (NoSerializerExistsException nse) {
densePart.add(nse.getIncompleteData());
for (int j = i + 1; j < denseCount; j++) {
densePart.add(BasicType.UNKNOWN);
}
retArray.setDenseValues(densePart);
throw new NoSerializerExistsException(nse.getClassName(), retArray, nse);
}
}
retArray.setDenseValues(densePart);
endDumpLevel();
LOGGER.log(Level.FINER, "Array value: dense_size={0},assocSize={1}", new Object[]{densePart.size(), assocPart.size()});
result = retArray;
} else {
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?
}
break;
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);
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) {
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 HashMap<>()), null);
}
newDumpLevel("serializedData", "U8[]");
MonitoredInputStream mis = new MonitoredInputStream(is);
Map<String, Object> 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);
LOGGER.log(Level.FINER, "Object/Traits value: customSerialized");
objectTable.add(retObjectType);
result = retObjectType;
break markerswitch;
} else {
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);
LOGGER.log(Level.FINEST, "object numSealed:{0}", numSealed);
String className = readUtf8Vr("className", stringTable);
LOGGER.log(Level.FINEST, "object className:{0}", className);
List<String> sealedMemberNames = new ArrayList<>();
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 {
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()});
}
Map<String, Object> sealedMembers = new ListMap<>();
Map<String, Object> dynamicMembers = new ListMap<>();
ObjectType retObjectType = new ObjectType(traits);
objectTable.add(retObjectType); //add it before any subvalue can reference it
List<Object> sealedMemberValues = new ArrayList<>();
NoSerializerExistsException error = null;
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;
}
}
endDumpLevel();
}
List<String> memberNames = new ArrayList<>();
memberNames.addAll(traits.getSealedMemberNames()); //Assuming it is ListSet so maintains order
for (int i = 0; i < memberNames.size(); i++) {
sealedMembers.put(memberNames.get(i), sealedMemberValues.get(i));
}
retObjectType.setSealedMembers(sealedMembers);
if (traits.isDynamic()) {
newDumpLevel("dynamicMembers", "dynamic-member[]");
String dynamicMemberName;
while (!(dynamicMemberName = readUtf8Vr("name", stringTable)).isEmpty()) {
try {
Object dynamicMemberValue = readValue("value", serializers, objectTable, traitsTable, stringTable);
dynamicMembers.put(dynamicMemberName, dynamicMemberValue);
} catch (NoSerializerExistsException nse) {
dynamicMembers.put(dynamicMemberName, nse.getIncompleteData());
retObjectType.setDynamicMembers(dynamicMembers);
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);
}
}
}
retObjectType.setDynamicMembers(dynamicMembers);
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 {
renameU29O_ref();
int refIndexObject = (int) (objectU29 >> 1);
LOGGER.log(Level.FINER, "Object value: reference({0})", refIndexObject);
result = objectTable.get(refIndexObject);
}
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) {
renameU29("U29X-value", NO_REFERENCE_BIT_TEXT, "byte length");
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 {
renameU29O_ref();
int refIndexXml = (int) (xmlU29 >> 1);
LOGGER.log(Level.FINER, "XML value: reference({0})", refIndexXml);
result = objectTable.get(refIndexXml);
}
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) {
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 {
renameU29O_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");
setDumpInfoType("vector-int-type");
LOGGER.log(Level.FINE, "Read value: vector_int");
long vectorIntU29 = readU29("U29");
int vectorIntNoRefFlag = (int) (vectorIntU29 & 1);
if (vectorIntNoRefFlag == 1) {
renameU29("U29V-value", NO_REFERENCE_BIT_TEXT, "item count");
int vectorIntCountItems = (int) (vectorIntU29 >> 1);
int fixed = readU8("fixed");
List<Long> vals = new ArrayList<>();
newDumpLevel("items", "S32[]");
for (int i = 0; i < vectorIntCountItems; i++) {
vals.add(readS32("intValue"));
}
endDumpLevel();
VectorIntType retVectorInt = new VectorIntType(fixed == 1, vals);
LOGGER.log(Level.FINER, "Vector<int> value: fixed={0}, size={1}]", new Object[]{fixed, vectorIntCountItems});
objectTable.add(retVectorInt);
result = retVectorInt;
} else {
renameU29O_ref();
int refIndexVectorInt = (int) (vectorIntU29 >> 1);
LOGGER.log(Level.FINER, "Vector<int> value: reference({0})", refIndexVectorInt);
result = objectTable.get(refIndexVectorInt);
}
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) {
renameU29("U29V-value", NO_REFERENCE_BIT_TEXT, "item count");
int vectorUIntCountItems = (int) (vectorUIntU29 >> 1);
int fixed = readU8("fixed");
List<Long> vals = new ArrayList<>();
newDumpLevel("items", "U32[]");
for (int i = 0; i < vectorUIntCountItems; i++) {
vals.add(readU32("uintValue"));
}
endDumpLevel();
VectorUIntType retVectorUInt = new VectorUIntType(fixed == 1, vals);
LOGGER.log(Level.FINER, "Vector<uint> value: fixed={0}, size={1}]", new Object[]{fixed, vectorUIntCountItems});
objectTable.add(retVectorUInt);
result = retVectorUInt;
} else {
renameU29O_ref();
int refIndexVectorUInt = (int) (vectorUIntU29 >> 1);
LOGGER.log(Level.FINER, "Vector<uint> value: reference({0})", refIndexVectorUInt);
result = objectTable.get(refIndexVectorUInt);
}
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) {
renameU29("U29V-value", NO_REFERENCE_BIT_TEXT, "item count");
int vectorDoubleCountItems = (int) (vectorDoubleU29 >> 1);
int fixed = readU8("fixed");
List<Double> vals = new ArrayList<>();
newDumpLevel("items", "DOUBLE[]");
for (int i = 0; i < vectorDoubleCountItems; i++) {
vals.add(readDouble("doubleValue"));
}
endDumpLevel();
VectorDoubleType retVectorDouble = new VectorDoubleType(fixed == 1, vals);
LOGGER.log(Level.FINER, "Vector<double> value: fixed={0}, size={1}]", new Object[]{fixed, vectorDoubleCountItems});
objectTable.add(retVectorDouble);
result = retVectorDouble;
} else {
renameU29O_ref();
int refIndexVectorDouble = (int) (vectorDoubleU29 >> 1);
LOGGER.log(Level.FINER, "Vector<double> value: reference({0})", refIndexVectorDouble);
result = objectTable.get(refIndexVectorDouble);
}
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) {
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<Object> 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));
} catch (NoSerializerExistsException nse) {
vals.add(nse.getIncompleteData());
for (int j = i + 1; j < vectorObjectCountItems; j++) {
vals.add(BasicType.UNKNOWN);
}
error = nse;
break;
}
}
endDumpLevel();
VectorObjectType retVectorObject = new VectorObjectType(fixed == 1, objectTypeName, vals);
LOGGER.log(Level.FINER, "Vector<Object> 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 {
renameU29O_ref();
int refIndexVectorObject = (int) (vectorObjectU29 >> 1);
LOGGER.log(Level.FINER, "Vector<Object> value: reference({0})", refIndexVectorObject);
result = objectTable.get(refIndexVectorObject);
}
break;
case Marker.DICTIONARY:
renameLastDump("dictionary-marker");
setDumpInfoType("dictionary-type");
long dictionaryObjectU29 = readU29("U29");
int dictionaryNoRefFlag = (int) (dictionaryObjectU29 & 1);
if (dictionaryNoRefFlag == 1) {
renameU29("U29Dict-value", NO_REFERENCE_BIT_TEXT, "entries count");
int numEntries = (int) (dictionaryObjectU29 >> 1);
int weakKeys = readU8("weak keys");
Map<Object, Object> data = new ListMap<>(true);
DictionaryType retDictionary = new DictionaryType(weakKeys == 1);
objectTable.add(retDictionary);
NoSerializerExistsException error = null;
newDumpLevel("entries", "");
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;
}
retDictionary.put(key, val);
if (error != null) {
for (int j = i + 1; j < numEntries; j++) {
retDictionary.put(BasicType.UNKNOWN, BasicType.UNKNOWN);
}
break;
}
}
endDumpLevel();
if (error != null) {
throw new NoSerializerExistsException(error.getClassName(), retDictionary, error);
}
result = retDictionary;
} else {
renameU29O_ref();
int refIndexDictionary = (int) (dictionaryObjectU29 >> 1);
LOGGER.log(Level.FINER, "Dictionary value: reference({0})", refIndexDictionary);
result = objectTable.get(refIndexDictionary);
}
break;
default:
throw new UnsupportedValueTypeException(marker);
}
} finally {
endDumpLevel();
}
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;
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();
}
}
}