mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-09-26 18:41:13 +00:00
AMF3 importer
refactoring AMFTools to AMF3 exporter
This commit is contained in:
@@ -2,8 +2,8 @@ package com.jpexs.decompiler.flash.amf.amf3;
|
||||
|
||||
public class Pair<T1, T2> {
|
||||
|
||||
private final T1 first;
|
||||
private final T2 second;
|
||||
private T1 first;
|
||||
private T2 second;
|
||||
|
||||
public Pair(T1 first, T2 second) {
|
||||
this.first = first;
|
||||
@@ -17,4 +17,12 @@ public class Pair<T1, T2> {
|
||||
public T2 getSecond() {
|
||||
return second;
|
||||
}
|
||||
|
||||
public void setSecond(T2 second) {
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
public void setFirst(T1 first) {
|
||||
this.first = first;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package com.jpexs.decompiler.flash.amf.amf3.types;
|
||||
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools;
|
||||
import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.WithSubValues;
|
||||
@@ -34,7 +34,7 @@ public abstract class AbstractVectorType<T> implements WithSubValues {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Amf3Tools.amfToString(this);
|
||||
return Amf3Exporter.amfToString(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.jpexs.decompiler.flash.amf.amf3.types;
|
||||
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools;
|
||||
import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Pair;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.util.ArrayList;
|
||||
@@ -27,7 +27,7 @@ public class ArrayType implements WithSubValues {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Amf3Tools.amfToString(this);
|
||||
return Amf3Exporter.amfToString(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.jpexs.decompiler.flash.amf.amf3.types;
|
||||
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools;
|
||||
import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter;
|
||||
|
||||
public class ByteArrayType {
|
||||
|
||||
@@ -16,7 +16,7 @@ public class ByteArrayType {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Amf3Tools.amfToString(this);
|
||||
return Amf3Exporter.amfToString(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.jpexs.decompiler.flash.amf.amf3.types;
|
||||
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools;
|
||||
import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter;
|
||||
import java.util.Date;
|
||||
|
||||
public class DateType {
|
||||
@@ -21,7 +21,7 @@ public class DateType {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Amf3Tools.amfToString(this);
|
||||
return Amf3Exporter.amfToString(this);
|
||||
}
|
||||
|
||||
public Date toDate() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.jpexs.decompiler.flash.amf.amf3.types;
|
||||
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools;
|
||||
import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Pair;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -32,7 +32,7 @@ public class DictionaryType implements WithSubValues {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Amf3Tools.amfToString(this);
|
||||
return Amf3Exporter.amfToString(this);
|
||||
}
|
||||
|
||||
public boolean hasWeakKeys() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.jpexs.decompiler.flash.amf.amf3.types;
|
||||
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools;
|
||||
import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Pair;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Traits;
|
||||
import com.jpexs.helpers.Helper;
|
||||
@@ -81,7 +81,7 @@ public class ObjectType implements WithSubValues {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Amf3Tools.amfToString(this);
|
||||
return Amf3Exporter.amfToString(this);
|
||||
}
|
||||
|
||||
public void setSerializedData(byte[] serializedData) {
|
||||
|
||||
+47
-41
@@ -1,5 +1,7 @@
|
||||
package com.jpexs.decompiler.flash.amf.amf3;
|
||||
package com.jpexs.decompiler.flash.exporters.amf.amf3;
|
||||
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Pair;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.WithSubValues;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.ArrayType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.XmlType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType;
|
||||
@@ -9,6 +11,7 @@ import com.jpexs.decompiler.flash.amf.amf3.types.ByteArrayType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.DateType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.DictionaryType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.BasicType;
|
||||
import com.jpexs.decompiler.flash.ecma.EcmaScript;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
@@ -18,20 +21,20 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Amf3Tools {
|
||||
public class Amf3Exporter {
|
||||
|
||||
/**
|
||||
* Converts AMF value to something human-readable.
|
||||
*
|
||||
* @param obj
|
||||
* @param amfValue
|
||||
* @return
|
||||
*/
|
||||
public static String amfToString(Object obj) {
|
||||
public static String amfToString(Object amfValue) {
|
||||
Map<Object, Integer> refCount = new HashMap<>();
|
||||
List<Object> objectList = new ArrayList<>();
|
||||
Map<Object, String> objectAlias = new HashMap<>();
|
||||
populateObjects(obj, refCount, objectList, objectAlias);
|
||||
return amfToString(new ArrayList<>(), 0, obj, refCount, objectAlias);
|
||||
populateObjects(amfValue, refCount, objectList, objectAlias);
|
||||
return amfToString(new ArrayList<>(), 0, amfValue, refCount, objectAlias);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,7 +92,7 @@ public class Amf3Tools {
|
||||
return "\"" + Helper.escapeActionScriptString((String) object) + "\"";
|
||||
}
|
||||
if (((List<? extends Class>) Arrays.asList(Long.class, Double.class, Boolean.class)).contains(object.getClass())) {
|
||||
return object.toString();
|
||||
return EcmaScript.toString(object);
|
||||
}
|
||||
|
||||
if (object instanceof BasicType) {
|
||||
@@ -105,7 +108,7 @@ public class Amf3Tools {
|
||||
}
|
||||
processedObjects.add(object);
|
||||
|
||||
String addId = refCount > 1 ? indent(level + 1) + "\"id\": \"" + objectAlias.get(object) + "\"\r\n" : "";
|
||||
String addId = refCount > 1 ? indent(level + 1) + "\"id\": \"" + objectAlias.get(object) + "\",\r\n" : "";
|
||||
|
||||
if (object instanceof AbstractVectorType) {
|
||||
AbstractVectorType avt = (AbstractVectorType) object;
|
||||
@@ -134,7 +137,7 @@ public class Amf3Tools {
|
||||
if (serData == null) {
|
||||
ret.append(indent(level + 1)).append("\"serialized\": unknown\r\n");
|
||||
} else {
|
||||
ret.append(indent(level + 1)).append("\"serialized\": 0x").append(javax.xml.bind.DatatypeConverter.printHexBinary(serData)).append(",\r\n");
|
||||
ret.append(indent(level + 1)).append("\"serialized\": \"").append(javax.xml.bind.DatatypeConverter.printHexBinary(serData)).append("\",\r\n");
|
||||
if (!ot.getSerializedMembers().isEmpty()) {
|
||||
ret.append(indent(level + 1)).append("\"unserializedMembers\": {\r\n");
|
||||
for (int i = 0; i < ot.getSerializedMembers().size(); i++) {
|
||||
@@ -152,41 +155,44 @@ public class Amf3Tools {
|
||||
}
|
||||
} else {
|
||||
ret.append(indent(level + 1)).append("\"dynamic\": ").append(ot.isDynamic()).append(",\r\n");
|
||||
if (!ot.getSealedMembers().isEmpty()) {
|
||||
ret.append(indent(level + 1)).append("\"sealedMembers\": {\r\n");
|
||||
for (int i = 0; i < ot.getSealedMembers().size(); i++) {
|
||||
Pair<String, Object> member = ot.getSealedMembers().get(i);
|
||||
ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 1, member.getSecond(), referenceCount, objectAlias));
|
||||
if (i < ot.getSealedMembers().size() - 1) {
|
||||
ret.append(",\r\n");
|
||||
} else {
|
||||
ret.append("\r\n");
|
||||
}
|
||||
//if (!ot.getSealedMembers().isEmpty()) {
|
||||
ret.append(indent(level + 1)).append("\"sealedMembers\": {\r\n");
|
||||
for (int i = 0; i < ot.getSealedMembers().size(); i++) {
|
||||
Pair<String, Object> member = ot.getSealedMembers().get(i);
|
||||
ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 1, member.getSecond(), referenceCount, objectAlias));
|
||||
if (i < ot.getSealedMembers().size() - 1) {
|
||||
ret.append(",\r\n");
|
||||
} else {
|
||||
ret.append("\r\n");
|
||||
}
|
||||
ret.append(indent(level + 1)).append("}");
|
||||
if (!ot.getDynamicMembers().isEmpty()) {
|
||||
}
|
||||
ret.append(indent(level + 1)).append("}");
|
||||
//if (!ot.getDynamicMembers().isEmpty()) {
|
||||
ret.append(",");
|
||||
//}
|
||||
ret.append("\r\n");
|
||||
//}
|
||||
//if (!ot.getDynamicMembers().isEmpty()) {
|
||||
ret.append(indent(level + 1)).append("\"dynamicMembers\": {\r\n");
|
||||
for (int i = 0; i < ot.getDynamicMembers().size(); i++) {
|
||||
Pair<String, Object> member = ot.getDynamicMembers().get(i);
|
||||
ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias));
|
||||
ret.append(":");
|
||||
ret.append(amfToString(processedObjects, level + 2, member.getSecond(), referenceCount, objectAlias));
|
||||
if (i < ot.getDynamicMembers().size() - 1) {
|
||||
ret.append(",");
|
||||
}
|
||||
ret.append("\r\n");
|
||||
}
|
||||
if (!ot.getDynamicMembers().isEmpty()) {
|
||||
ret.append(indent(level + 1)).append("\"dynamicMembers\": {\r\n");
|
||||
for (int i = 0; i < ot.getDynamicMembers().size(); i++) {
|
||||
Pair<String, Object> member = ot.getDynamicMembers().get(i);
|
||||
ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 2, member.getSecond(), referenceCount, objectAlias));
|
||||
if (i < ot.getDynamicMembers().size() - 1) {
|
||||
ret.append(",");
|
||||
}
|
||||
ret.append("\r\n");
|
||||
}
|
||||
ret.append(indent(level + 1)).append("}\r\n");
|
||||
}
|
||||
ret.append(indent(level + 1)).append("}\r\n");
|
||||
//}
|
||||
}
|
||||
ret.append(indent(level)).append("}");
|
||||
} else if (object instanceof ArrayType) {
|
||||
ArrayType at = (ArrayType) object;
|
||||
ret.append("{\r\n");
|
||||
ret.append(indent(level + 1)).append("\"type\": \"Array\",\r\n");
|
||||
ret.append(addId);
|
||||
ret.append(indent(level + 1)).append("\"denseValues\": [");
|
||||
|
||||
for (int i = 0; i < at.getDenseValues().size(); i++) {
|
||||
@@ -196,7 +202,7 @@ public class Amf3Tools {
|
||||
ret.append(amfToString(processedObjects, level + 2, at.getDenseValues().get(i), referenceCount, objectAlias));
|
||||
}
|
||||
ret.append("],\r\n");
|
||||
ret.append(indent(level + 1)).append("\"associativeValues\": [");
|
||||
ret.append(indent(level + 1)).append("\"associativeValues\": {");
|
||||
if (!at.getAssociativeValues().isEmpty()) {
|
||||
ret.append("\r\n");
|
||||
}
|
||||
@@ -211,15 +217,15 @@ public class Amf3Tools {
|
||||
if (!at.getAssociativeValues().isEmpty()) {
|
||||
ret.append(indent(level + 1));
|
||||
}
|
||||
ret.append("]\r\n");
|
||||
ret.append("}\r\n");
|
||||
ret.append(indent(level)).append("}");
|
||||
} else if (object instanceof DictionaryType) {
|
||||
DictionaryType dt = (DictionaryType) object;
|
||||
ret.append("{\r\n");
|
||||
ret.append(indent(level + 1)).append("\"type\": \"Dictionary\",\r\n");
|
||||
ret.append(addId);
|
||||
ret.append(indent(level + 1)).append("\"weakKeys\": " + dt.hasWeakKeys() + ",\r\n");
|
||||
ret.append(indent(level + 1)).append("\"value\": {\r\n");
|
||||
ret.append(indent(level + 1)).append("\"weakKeys\": ").append(dt.hasWeakKeys()).append(",\r\n");
|
||||
ret.append(indent(level + 1)).append("\"entries\": {\r\n");
|
||||
for (int i = 0; i < dt.getPairs().size(); i++) {
|
||||
Pair<Object, Object> pair = dt.getPairs().get(i);
|
||||
ret.append(indent(level + 1)).append(amfToString(processedObjects, level + 1, pair.getFirst(), referenceCount, objectAlias)).append(" : ").append(amfToString(processedObjects, level + 1, pair.getSecond(), referenceCount, objectAlias));
|
||||
@@ -229,14 +235,14 @@ public class Amf3Tools {
|
||||
ret.append("\r\n");
|
||||
}
|
||||
ret.append(indent(level + 1)).append("}\r\n");
|
||||
ret.append(indent(level) + "}");
|
||||
ret.append(indent(level)).append("}");
|
||||
} else if (object instanceof ByteArrayType) {
|
||||
ByteArrayType ba = (ByteArrayType) object;
|
||||
byte data[] = ba.getData();
|
||||
return "{\r\n"
|
||||
+ indent(level + 1) + "\"type\": \"ByteArray\",\r\n"
|
||||
+ addId
|
||||
+ indent(level + 1) + "\"value\": 0x" + javax.xml.bind.DatatypeConverter.printHexBinary(data) + "\r\n"
|
||||
+ indent(level + 1) + "\"value\": \"" + javax.xml.bind.DatatypeConverter.printHexBinary(data) + "\"\r\n"
|
||||
+ indent(level) + "}";
|
||||
} else if (object instanceof DateType) {
|
||||
DateType dt = (DateType) object;
|
||||
@@ -249,13 +255,13 @@ public class Amf3Tools {
|
||||
|
||||
} else if (object instanceof XmlDocType) {
|
||||
return "{\r\n"
|
||||
+ indent(level + 1) + "\"type\": \"XML\",\r\n"
|
||||
+ indent(level + 1) + "\"type\": \"XMLDocument\",\r\n"
|
||||
+ addId
|
||||
+ indent(level + 1) + "\"value\": " + amfToString(processedObjects, level, ((XmlDocType) object).getData(), referenceCount, objectAlias) + "\r\n"
|
||||
+ indent(level) + "}";
|
||||
} else if (object instanceof XmlType) {
|
||||
return "{\r\n"
|
||||
+ indent(level + 1) + "\"type\": \"XMLDocument\",\r\n"
|
||||
+ indent(level + 1) + "\"type\": \"XML\",\r\n"
|
||||
+ addId
|
||||
+ indent(level + 1) + "\"value\": " + amfToString(processedObjects, level, ((XmlType) object).getData(), referenceCount, objectAlias) + "\r\n"
|
||||
+ indent(level) + "}";
|
||||
@@ -0,0 +1,556 @@
|
||||
package com.jpexs.decompiler.flash.importers.amf.amf3;
|
||||
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Pair;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Traits;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.ArrayType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.BasicType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.ByteArrayType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.DateType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.DictionaryType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.VectorDoubleType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.VectorIntType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.VectorObjectType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.VectorUIntType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.XmlDocType;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.types.XmlType;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Amf3Importer {
|
||||
|
||||
private Amf3Lexer lexer;
|
||||
|
||||
private ParsedSymbol lex() throws IOException, Amf3ParseException {
|
||||
ParsedSymbol ret = lexer.lex();
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void pushback(ParsedSymbol s) {
|
||||
lexer.pushback(s);
|
||||
}
|
||||
|
||||
private void expected(ParsedSymbol symb, int line, Object... expected) throws IOException, Amf3ParseException {
|
||||
boolean found = false;
|
||||
for (Object t : expected) {
|
||||
if (symb.type == t) {
|
||||
found = true;
|
||||
}
|
||||
if (symb.group == t) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
String expStr = "";
|
||||
boolean first = true;
|
||||
for (Object e : expected) {
|
||||
if (!first) {
|
||||
expStr += " or ";
|
||||
}
|
||||
expStr += e;
|
||||
first = false;
|
||||
}
|
||||
throw new Amf3ParseException("" + expStr + " expected but " + symb.type + " found", line);
|
||||
}
|
||||
}
|
||||
|
||||
private ParsedSymbol expectedType(Object... type) throws IOException, Amf3ParseException {
|
||||
ParsedSymbol symb = lex();
|
||||
expected(symb, lexer.yyline(), type);
|
||||
return symb;
|
||||
}
|
||||
|
||||
private JsArray parseArray(Map<String, Object> objectTable) throws IOException, Amf3ParseException {
|
||||
expectedType(SymbolType.BRACKET_OPEN);
|
||||
List<Object> arrayVals = new ArrayList<>();
|
||||
ParsedSymbol s = lex();
|
||||
if (!s.isType(SymbolType.BRACKET_CLOSE)) {
|
||||
pushback(s);
|
||||
arrayVals.add(value(objectTable));
|
||||
s = lex();
|
||||
while (s.isType(SymbolType.COMMA)) {
|
||||
arrayVals.add(value(objectTable));
|
||||
s = lex();
|
||||
}
|
||||
pushback(s);
|
||||
}
|
||||
expectedType(SymbolType.BRACKET_CLOSE);
|
||||
return new JsArray(arrayVals);
|
||||
}
|
||||
|
||||
private class JsArray {
|
||||
|
||||
private List<Object> values = new ArrayList<>();
|
||||
|
||||
public JsArray() {
|
||||
}
|
||||
|
||||
public JsArray(List<Object> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public void add(Object value) {
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
public List<Object> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class JsObject {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("{");
|
||||
for (Pair<Object, Object> p : values) {
|
||||
sb.append(p.getFirst()).append(":").append("?").append(",\r\n");
|
||||
}
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private final List<Pair<Object, Object>> values = new ArrayList<>();
|
||||
|
||||
public void add(Object key, Object value) {
|
||||
values.add(new Pair<>(key, value));
|
||||
}
|
||||
|
||||
public String getString(Object key) throws Amf3ParseException {
|
||||
return (String) getRequired(key, "String");
|
||||
}
|
||||
|
||||
public Boolean getBoolean(Object key) throws Amf3ParseException {
|
||||
return (Boolean) getRequired(key, "Boolean");
|
||||
}
|
||||
|
||||
public JsObject getJsObject(Object key) throws Amf3ParseException {
|
||||
return (JsObject) getRequired(key, "JsObject");
|
||||
}
|
||||
|
||||
public JsArray getJsArray(Object key) throws Amf3ParseException {
|
||||
return (JsArray) getRequired(key, "JsArray");
|
||||
}
|
||||
|
||||
public List<Object> getJsArrayOfObject(Object key) throws Amf3ParseException {
|
||||
return getJsArray(key).getValues();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<String> getJsArrayOfString(Object key) throws Amf3ParseException {
|
||||
return (List<String>) getJsArray(key, "String");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Long> getJsArrayOfInt(Object key) throws Amf3ParseException {
|
||||
return (List<Long>) getJsArray(key, "int");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
public List<Long> getJsArrayOfUint(Object key) throws Amf3ParseException {
|
||||
return (List<Long>) getJsArray(key, "uint");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Double> getJsArrayOfNumber(Object key) throws Amf3ParseException {
|
||||
return (List<Double>) getJsArray(key, "Number");
|
||||
}
|
||||
|
||||
public List getJsArray(Object key, String valueType) throws Amf3ParseException {
|
||||
JsArray jsArr = (JsArray) getRequired(key, "JsArray");
|
||||
switch (valueType) {
|
||||
case "String":
|
||||
List<String> stringList = new ArrayList<>();
|
||||
for (Object v : jsArr.getValues()) {
|
||||
String sv = null;
|
||||
if (v instanceof String) {
|
||||
sv = (String) v;
|
||||
} else {
|
||||
throw new Amf3ParseException("Not String: " + v, 0);
|
||||
}
|
||||
stringList.add(sv);
|
||||
}
|
||||
return stringList;
|
||||
case "int":
|
||||
case "uint":
|
||||
List<Long> longList = new ArrayList<>();
|
||||
for (Object v : jsArr.getValues()) {
|
||||
Long lv = null;
|
||||
if (v instanceof Long) {
|
||||
lv = (Long) v;
|
||||
} else {
|
||||
throw new Amf3ParseException("Not an Integer value: " + v, 0);
|
||||
}
|
||||
if (valueType.equals("uint") && lv < 0) {
|
||||
throw new Amf3ParseException("Not an Unsigned Integer value: " + v, 0);
|
||||
}
|
||||
longList.add(lv);
|
||||
}
|
||||
return longList;
|
||||
case "Number":
|
||||
List<Double> doubleList = new ArrayList<>();
|
||||
for (Object v : jsArr.getValues()) {
|
||||
Double cv = null;
|
||||
if (v instanceof Long) {
|
||||
cv = (double) (long) (Long) v;
|
||||
} else if (v instanceof Double) {
|
||||
cv = (Double) v;
|
||||
} else {
|
||||
throw new Amf3ParseException("Not a Number: " + v, 0);
|
||||
}
|
||||
doubleList.add(cv);
|
||||
}
|
||||
return doubleList;
|
||||
default:
|
||||
throw new Amf3ParseException("Unsupported array value type: " + valueType, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public Long getLong(Object key) throws Amf3ParseException {
|
||||
return (Long) getRequired(key, "Long");
|
||||
}
|
||||
|
||||
public Object getRequired(Object key, String requiredType) throws Amf3ParseException {
|
||||
if (!containsKey(key)) {
|
||||
throw new Amf3ParseException("\"" + key + "\" is missing", 0);
|
||||
}
|
||||
Object val = get(key);
|
||||
boolean typeMatches = true;
|
||||
if (requiredType != null) {
|
||||
switch (requiredType) {
|
||||
case "String":
|
||||
typeMatches = val instanceof String;
|
||||
break;
|
||||
case "Long":
|
||||
typeMatches = val instanceof Long;
|
||||
break;
|
||||
case "JsObject":
|
||||
typeMatches = val instanceof JsObject;
|
||||
break;
|
||||
case "JsArray":
|
||||
typeMatches = val instanceof JsArray;
|
||||
break;
|
||||
case "Boolean":
|
||||
typeMatches = val instanceof Boolean;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!typeMatches) {
|
||||
throw new Amf3ParseException("\"" + key + "\" value must be of type " + requiredType, 0);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
for (Pair<Object, Object> p : values) {
|
||||
if (p.getFirst().equals(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object get(Object key) {
|
||||
for (Pair<Object, Object> p : values) {
|
||||
if (p.getFirst().equals(key)) {
|
||||
return p.getSecond();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void resolve(Object key, Map<String, Object> objectTable, boolean allowTypedObject) throws Amf3ParseException {
|
||||
for (Pair<Object, Object> p : values) {
|
||||
if (p.getFirst().equals(key)) {
|
||||
Object resolved = resolveObjects(p.getSecond(), objectTable, allowTypedObject);
|
||||
p.setSecond(resolved);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> stringKeys() {
|
||||
List<String> ret = new ArrayList<>();
|
||||
for (Pair<Object, Object> p : values) {
|
||||
if (p.getFirst() instanceof String) {
|
||||
ret.add((String) p.getFirst());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public List<Pair<Object, Object>> getAll() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public List<Pair<String, Object>> getStringMapped() {
|
||||
List<Pair<String, Object>> ret = new ArrayList<>();
|
||||
for (Pair<Object, Object> p : values) {
|
||||
if (p.getFirst() instanceof String) {
|
||||
String keyStr = (String) p.getFirst();
|
||||
ret.add(new Pair<>(keyStr, p.getSecond()));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Map<String, Object> getStringMap() {
|
||||
Map<String, Object> ret = new HashMap<>();
|
||||
for (Pair<Object, Object> p : values) {
|
||||
if (p.getFirst() instanceof String) {
|
||||
String keyStr = (String) p.getFirst();
|
||||
ret.put(keyStr, values);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
private JsObject parseObject(Map<String, Object> objectTable) throws IOException, Amf3ParseException {
|
||||
JsObject ret = new JsObject();
|
||||
|
||||
expectedType(SymbolType.CURLY_OPEN);
|
||||
ParsedSymbol s = lex();
|
||||
if (!s.isType(SymbolType.CURLY_CLOSE)) {
|
||||
pushback(s);
|
||||
do {
|
||||
Object key = value(objectTable);
|
||||
expectedType(SymbolType.COLON);
|
||||
Object value = value(objectTable);
|
||||
ret.add(key, value);
|
||||
if ("id".equals(key)) {
|
||||
if (!(value instanceof String)) {
|
||||
throw new Amf3ParseException("id must be string value", lexer.yyline());
|
||||
}
|
||||
objectTable.put((String) value, BasicType.UNDEFINED);
|
||||
}
|
||||
s = lex();
|
||||
} while (s.isType(SymbolType.COMMA));
|
||||
}
|
||||
pushback(s);
|
||||
expectedType(SymbolType.CURLY_CLOSE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private Object resolveObjects(Object object, Map<String, Object> objectTable, boolean allowTypedObject) throws Amf3ParseException {
|
||||
Object resultObject = object;
|
||||
if (object instanceof JsArray) {
|
||||
JsArray jsa = (JsArray) object;
|
||||
JsArray ret = new JsArray();
|
||||
for (int i = 0; i < jsa.values.size(); i++) {
|
||||
ret.values.add(resolveObjects(jsa.values.get(i), objectTable, true));
|
||||
}
|
||||
resultObject = ret;
|
||||
} else if (object instanceof JsObject) {
|
||||
if (allowTypedObject) {
|
||||
JsObject typedObject = (JsObject) object;
|
||||
if (typedObject.containsKey("type")) {
|
||||
String typeStr = typedObject.getString("type");
|
||||
String id = typedObject.containsKey("id") ? typedObject.getString("id") : null;
|
||||
switch (typeStr) {
|
||||
case "Date":
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS");
|
||||
String dateStr = typedObject.getString("value");
|
||||
try {
|
||||
resultObject = new DateType((double) sdf.parse(dateStr).getTime());
|
||||
} catch (ParseException ex) {
|
||||
throw new Amf3ParseException("Invalid date format: " + dateStr, lexer.yyline());
|
||||
}
|
||||
break;
|
||||
case "XML":
|
||||
resultObject = new XmlType(typedObject.getString("value"));
|
||||
break;
|
||||
case "XMLDocument":
|
||||
resultObject = new XmlDocType(typedObject.getString("value"));
|
||||
break;
|
||||
case "Object":
|
||||
String className = typedObject.getString("className");
|
||||
if (typedObject.containsKey("serialized")) {
|
||||
//TODO
|
||||
} else {
|
||||
boolean dynamic = typedObject.getBoolean("dynamic");
|
||||
typedObject.resolve("sealedMembers", objectTable, false);
|
||||
JsObject jsoSealed = typedObject.getJsObject("sealedMembers");
|
||||
|
||||
List<Pair<String, Object>> sealedMembers = jsoSealed.getStringMapped();
|
||||
typedObject.resolve("dynamicMembers", objectTable, false);
|
||||
List<Pair<String, Object>> dynamicMembers = typedObject.getJsObject("dynamicMembers").getStringMapped();
|
||||
|
||||
List<String> sealedMemberNames = new ArrayList<>(jsoSealed.stringKeys());
|
||||
resultObject = new ObjectType(new Traits(className, dynamic, sealedMemberNames), sealedMembers, dynamicMembers);
|
||||
}
|
||||
break;
|
||||
case "Array":
|
||||
typedObject.resolve("denseValues", objectTable, false);
|
||||
List<Object> denseValues = typedObject.getJsArray("denseValues").getValues();
|
||||
typedObject.resolve("associativeValues", objectTable, false);
|
||||
JsObject resolvedArr = typedObject.getJsObject("associativeValues");
|
||||
List<Pair<String, Object>> associativeValues = resolvedArr.getStringMapped();
|
||||
resultObject = new ArrayType(denseValues, associativeValues);
|
||||
break;
|
||||
|
||||
case "Vector":
|
||||
boolean fixed = typedObject.getBoolean("fixed");
|
||||
String subtype = typedObject.getString("subtype");
|
||||
typedObject.resolve("values", objectTable, false);
|
||||
switch (subtype) {
|
||||
case "int":
|
||||
resultObject = new VectorIntType(fixed, typedObject.getJsArrayOfInt("values"));
|
||||
break;
|
||||
case "uint":
|
||||
resultObject = new VectorUIntType(fixed, typedObject.getJsArrayOfUint("values"));
|
||||
break;
|
||||
case "Number":
|
||||
resultObject = new VectorDoubleType(fixed, typedObject.getJsArrayOfNumber("values"));
|
||||
break;
|
||||
default:
|
||||
resultObject = new VectorObjectType(fixed, subtype, typedObject.getJsArrayOfObject("values"));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "ByteArray":
|
||||
try {
|
||||
resultObject = new ByteArrayType(javax.xml.bind.DatatypeConverter.parseHexBinary(typedObject.getString("value")));
|
||||
} catch (IllegalArgumentException iex) {
|
||||
throw new Amf3ParseException("Invalid hex byte sequence", lexer.yyline());
|
||||
}
|
||||
break;
|
||||
case "Dictionary":
|
||||
boolean weakKeys = typedObject.getBoolean("weakKeys");
|
||||
typedObject.resolve("entries", objectTable, false);
|
||||
List<Pair<Object, Object>> entries = typedObject.getJsObject("entries").getAll();
|
||||
resultObject = new DictionaryType(weakKeys, entries);
|
||||
break;
|
||||
default:
|
||||
throw new Amf3ParseException("Unknown object type: " + typeStr, lexer.yyline());
|
||||
}
|
||||
if (id != null) {
|
||||
objectTable.put(id, resultObject);
|
||||
}
|
||||
}
|
||||
} else { //not allowTypeObject
|
||||
JsObject jsObject = (JsObject) object;
|
||||
for (Pair<Object, Object> p : jsObject.getAll()) {
|
||||
p.setFirst(resolveObjects(p.getFirst(), objectTable, true));
|
||||
p.setSecond(resolveObjects(p.getSecond(), objectTable, true));
|
||||
}
|
||||
resultObject = jsObject;
|
||||
}
|
||||
}
|
||||
return resultObject;
|
||||
}
|
||||
|
||||
private Object value(Map<String, Object> objectTable) throws IOException, Amf3ParseException {
|
||||
ParsedSymbol s = lex();
|
||||
switch (s.type) {
|
||||
case CURLY_OPEN:
|
||||
pushback(s);
|
||||
return parseObject(objectTable);
|
||||
case BRACKET_OPEN:
|
||||
pushback(s);
|
||||
return parseArray(objectTable);
|
||||
case STRING:
|
||||
case DOUBLE:
|
||||
case INTEGER:
|
||||
return s.value;
|
||||
case UNDEFINED:
|
||||
return BasicType.UNDEFINED;
|
||||
case NULL:
|
||||
return BasicType.NULL;
|
||||
case UNKNOWN:
|
||||
return BasicType.UNKNOWN;
|
||||
case TRUE:
|
||||
return Boolean.TRUE;
|
||||
case FALSE:
|
||||
return Boolean.FALSE;
|
||||
case REFERENCE:
|
||||
String referencedId = (String) s.value;
|
||||
return new ReferencedObjectType(referencedId);
|
||||
default:
|
||||
throw new Amf3ParseException("Unexpected symbol: " + s, lexer.yyline());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deeply replace all ReferencedObjectType with the correct value
|
||||
*
|
||||
* @param object
|
||||
* @param objectsTable
|
||||
* @return
|
||||
*/
|
||||
private Object replaceReferences(Object object, Map<String, Object> objectsTable) throws Amf3ParseException {
|
||||
if (object instanceof ReferencedObjectType) {
|
||||
String key = ((ReferencedObjectType) object).key;
|
||||
if (!objectsTable.containsKey(key)) {
|
||||
throw new Amf3ParseException("Reference to undefined object: #" + key, 0);
|
||||
}
|
||||
return objectsTable.get(key);
|
||||
} else if (object instanceof ObjectType) {
|
||||
ObjectType ot = (ObjectType) object;
|
||||
for (Pair<String, Object> p : ot.getSealedMembers()) {
|
||||
p.setSecond(replaceReferences(p.getSecond(), objectsTable));
|
||||
}
|
||||
for (Pair<String, Object> p : ot.getDynamicMembers()) {
|
||||
p.setSecond(replaceReferences(p.getSecond(), objectsTable));
|
||||
}
|
||||
} else if (object instanceof ArrayType) {
|
||||
ArrayType at = (ArrayType) object;
|
||||
for (Pair<String, Object> p : at.getAssociativeValues()) {
|
||||
p.setSecond(replaceReferences(p.getSecond(), objectsTable));
|
||||
}
|
||||
for (int i = 0; i < at.getDenseValues().size(); i++) {
|
||||
at.getDenseValues().set(i, replaceReferences(at.getDenseValues().get(i), objectsTable));
|
||||
}
|
||||
} else if (object instanceof VectorObjectType) {
|
||||
VectorObjectType vot = (VectorObjectType) object;
|
||||
for (int i = 0; i < vot.getValues().size(); i++) {
|
||||
vot.getValues().set(i, replaceReferences(vot.getValues().get(i), objectsTable));
|
||||
}
|
||||
} else if (object instanceof DictionaryType) {
|
||||
DictionaryType dt = (DictionaryType) object;
|
||||
for (Pair<Object, Object> p : dt.getPairs()) {
|
||||
p.setFirst(replaceReferences(p.getFirst(), objectsTable));
|
||||
p.setSecond(replaceReferences(p.getSecond(), objectsTable));
|
||||
}
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
private class ReferencedObjectType {
|
||||
|
||||
private final String key;
|
||||
|
||||
public ReferencedObjectType(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "#" + key;
|
||||
}
|
||||
}
|
||||
|
||||
public Object stringToAmf(String val) throws IOException, Amf3ParseException {
|
||||
lexer = new Amf3Lexer(new StringReader(val));
|
||||
Map<String, Object> objectsTable = new HashMap<>();
|
||||
List<ReferencedObjectType> references = new ArrayList<>();
|
||||
Object result = value(objectsTable);
|
||||
Object resultResolved = resolveObjects(result, objectsTable, true);
|
||||
Object resultNoRef = replaceReferences(resultResolved, objectsTable);
|
||||
return resultNoRef;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.importers.amf.amf3;
|
||||
|
||||
import com.jpexs.decompiler.flash.ParseException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class Amf3ParseException extends ParseException {
|
||||
|
||||
public Amf3ParseException(String text, long line) {
|
||||
super(text, line);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.importers.amf.amf3;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ParsedSymbol {
|
||||
|
||||
public SymbolGroup group;
|
||||
|
||||
public Object value;
|
||||
|
||||
public SymbolType type;
|
||||
|
||||
public ParsedSymbol(SymbolGroup group, SymbolType type) {
|
||||
this.group = group;
|
||||
this.type = type;
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
public ParsedSymbol(SymbolGroup group, SymbolType type, Object value) {
|
||||
this.group = group;
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return group.toString() + " " + type.toString() + " " + (value != null ? value.toString() : "");
|
||||
}
|
||||
|
||||
public boolean isType(Object... types) {
|
||||
for (Object t : types) {
|
||||
if (t instanceof SymbolGroup) {
|
||||
if (group == t) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (t instanceof SymbolType) {
|
||||
if (type == t) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.importers.amf.amf3;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public enum SymbolGroup {
|
||||
OPERATOR,
|
||||
KEYWORD,
|
||||
STRING,
|
||||
COMMENT,
|
||||
IDENTIFIER,
|
||||
INTEGER,
|
||||
DOUBLE,
|
||||
EOF,
|
||||
GLOBALCONST,
|
||||
UNKNOWN
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2016 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.importers.amf.amf3;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public enum SymbolType {
|
||||
NULL,
|
||||
UNDEFINED,
|
||||
FALSE,
|
||||
TRUE,
|
||||
CURLY_OPEN,
|
||||
CURLY_CLOSE,
|
||||
BRACKET_OPEN,
|
||||
BRACKET_CLOSE,
|
||||
COMMA,
|
||||
COLON,
|
||||
UNKNOWN,
|
||||
REFERENCE,
|
||||
IDENTIFIER,
|
||||
INTEGER,
|
||||
STRING,
|
||||
DOUBLE,
|
||||
EOF
|
||||
}
|
||||
Reference in New Issue
Block a user