From 0d8e621a17a1ff8c73dcc728a2d9b5c63204a098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Tue, 19 Jul 2016 22:21:47 +0200 Subject: [PATCH] AMF3 importer refactoring AMFTools to AMF3 exporter --- libsrc/ffdec_lib/lexers/amf3.flex | 191 +++ .../jpexs/decompiler/flash/amf/amf3/Pair.java | 12 +- .../amf/amf3/types/AbstractVectorType.java | 4 +- .../flash/amf/amf3/types/ArrayType.java | 4 +- .../flash/amf/amf3/types/ByteArrayType.java | 4 +- .../flash/amf/amf3/types/DateType.java | 4 +- .../flash/amf/amf3/types/DictionaryType.java | 4 +- .../flash/amf/amf3/types/ObjectType.java | 4 +- .../amf/amf3/Amf3Exporter.java} | 88 +- .../importers/amf/amf3/Amf3Importer.java | 556 +++++++++ .../flash/importers/amf/amf3/Amf3Lexer.java | 1033 +++++++++++++++++ .../amf/amf3/Amf3ParseException.java | 30 + .../importers/amf/amf3/ParsedSymbol.java | 63 + .../flash/importers/amf/amf3/SymbolGroup.java | 34 + .../flash/importers/amf/amf3/SymbolType.java | 41 + .../flash/amf/amf3/Amf3OutputStreamTest.java | 12 - .../amf/amf3/parser/Amf3ImporterTest.java | 85 ++ libsrc/ffdec_lib/testdata/amf3/AmfTest.as | 3 + libsrc/ffdec_lib/testdata/amf3/AmfTest.swf | Bin 2287 -> 2302 bytes .../ffdec_lib/testdata/amf3/generated/all.bin | Bin 765 -> 765 bytes .../testdata/amf3/generated/custom.bin | 4 +- .../generated/noserializer_dictionary_key.bin | Bin 68 -> 68 bytes .../testdata/amf3/generated/simple.bin | 2 + 23 files changed, 2109 insertions(+), 69 deletions(-) create mode 100644 libsrc/ffdec_lib/lexers/amf3.flex rename libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/{amf/amf3/Amf3Tools.java => exporters/amf/amf3/Amf3Exporter.java} (80%) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Importer.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Lexer.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3ParseException.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/ParsedSymbol.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolGroup.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolType.java create mode 100644 libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/importers/amf/amf3/parser/Amf3ImporterTest.java create mode 100644 libsrc/ffdec_lib/testdata/amf3/generated/simple.bin diff --git a/libsrc/ffdec_lib/lexers/amf3.flex b/libsrc/ffdec_lib/lexers/amf3.flex new file mode 100644 index 000000000..7cbcd3fd2 --- /dev/null +++ b/libsrc/ffdec_lib/lexers/amf3.flex @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.amf.amf3; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +%% + +%public +%class Amf3Lexer +%final +%unicode +%char +%type ParsedSymbol +%throws Amf3ParseException + +%{ + + StringBuilder string = new StringBuilder(); + + private static String xmlTagName = ""; + + public int yychar() { + return yychar; + } + + private final Stack pushedBack = new Stack<>(); + + public int yyline() { + return yyline + 1; + } + + public void pushback(ParsedSymbol symb) { + pushedBack.push(symb); + last = null; + } + + ParsedSymbol last; + public ParsedSymbol lex() throws java.io.IOException, Amf3ParseException{ + ParsedSymbol ret = null; + if (!pushedBack.isEmpty()){ + ret = last = pushedBack.pop(); + } else { + ret = last = yylex(); + } + return ret; + } + +%} + +/* main character classes */ +LineTerminator = \r|\n|\r\n +InputCharacter = [^\r\n] + +WhiteSpace = {LineTerminator} | [ \t\f]+ + +/* comments */ +Comment = {TraditionalComment} | {EndOfLineComment} + +TraditionalComment = "/*" [^*] ~"*/" | "/*" "*"+ "/" +EndOfLineComment = "//" {InputCharacter}* {LineTerminator}? + + + +/* identifiers */ +Identifier = [:jletter:][:jletterdigit:]* + +Reference = "#" {Identifier} + +/* integer literals */ +DecIntegerLiteral = 0 | "-"? [1-9][0-9]* + +HexIntegerLiteral = 0 [xX] 0* {HexDigit} {1,8} +HexDigit = [0-9a-fA-F] + +OctIntegerLiteral = 0+ [1-3]? {OctDigit} {1,15} +OctDigit = [0-7] + +/* floating point literals */ +DoubleLiteral = "-"? ({FLit1}|{FLit2}|{FLit3}) {Exponent}? + +FLit1 = [0-9]+ \. [0-9]* +FLit2 = \. [0-9]+ +FLit3 = [0-9]+ +Exponent = [eE] [+-]? [0-9]+ + +/* string and character literals */ +StringCharacter = [^\r\n\"\\] + +%state STRING, CHARLITERAL + +%% + + { + + /* keywords */ + "null" { return new ParsedSymbol(SymbolGroup.GLOBALCONST, SymbolType.NULL, yytext()); } + "undefined" { return new ParsedSymbol(SymbolGroup.GLOBALCONST, SymbolType.UNDEFINED, yytext()); } + "false" { return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.FALSE, yytext()); } + "true" { return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.TRUE, yytext()); } + "unknown" { return new ParsedSymbol(SymbolGroup.UNKNOWN, SymbolType.UNKNOWN, yytext()); } + + + /* operators */ + + "{" { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.CURLY_OPEN, yytext()); } + "}" { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.CURLY_CLOSE, yytext()); } + "[" { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.BRACKET_OPEN, yytext()); } + "]" { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.BRACKET_CLOSE, yytext()); } + "," { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.COMMA, yytext()); } + ":" { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.COLON, yytext()); } + + /* string literal */ + \" { + string.setLength(0); + yybegin(STRING); + } + + + /* numeric literals */ + + {DecIntegerLiteral} { return new ParsedSymbol(SymbolGroup.INTEGER, SymbolType.INTEGER, Long.parseLong((yytext()))); } + + {HexIntegerLiteral} { return new ParsedSymbol(SymbolGroup.INTEGER, SymbolType.INTEGER, Long.parseLong(yytext().substring(2), 16)); } + + {OctIntegerLiteral} { return new ParsedSymbol(SymbolGroup.INTEGER, SymbolType.INTEGER, Long.parseLong(yytext(), 8)); } + + {DoubleLiteral} { return new ParsedSymbol(SymbolGroup.DOUBLE, SymbolType.DOUBLE, Double.parseDouble((yytext()))); } + + /* comments */ + {Comment} { /*ignore*/ } + + {LineTerminator} { yyline++;} + /* whitespace */ + {WhiteSpace} { /*ignore*/ } + + /* identifiers */ + {Reference} { return new ParsedSymbol(SymbolGroup.IDENTIFIER, SymbolType.REFERENCE, yytext().substring(1)); } + {Identifier} { return new ParsedSymbol(SymbolGroup.IDENTIFIER, SymbolType.IDENTIFIER, yytext()); } +} + + { + \" { + yybegin(YYINITIAL); + // length also includes the trailing quote + return new ParsedSymbol(SymbolGroup.STRING, SymbolType.STRING, string.toString()); + } + + {StringCharacter}+ { string.append(yytext()); } + + /* escape sequences */ + "\\b" { string.append('\b'); } + "\\t" { string.append('\t'); } + "\\n" { string.append('\n'); } + "\\f" { string.append('\f'); } + "\\r" { string.append('\r'); } + "\\\"" { string.append('\"'); } + "\\'" { string.append('\''); } + "\\\\" { string.append('\\'); } + \\x{HexDigit}{2} { char val = (char) Integer.parseInt(yytext().substring(2), 16); + string.append(val); } + \\u{HexDigit}{4} { char val = (char) Integer.parseInt(yytext().substring(2), 16); + string.append(val); } + \\{OctDigit}{3} { char val = (char) Integer.parseInt(yytext().substring(1), 8); + string.append(val); } + + /* escape sequences */ + + \\. { string.append('\\'); /*illegal escape sequence*/ } + {LineTerminator} { yybegin(YYINITIAL); yyline++;} +} + +/* error fallback */ +[^] { } +<> { return new ParsedSymbol(SymbolGroup.EOF, SymbolType.EOF, null); } + diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Pair.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Pair.java index 021814ec2..81e7dd7b1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Pair.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Pair.java @@ -2,8 +2,8 @@ package com.jpexs.decompiler.flash.amf.amf3; public class Pair { - private final T1 first; - private final T2 second; + private T1 first; + private T2 second; public Pair(T1 first, T2 second) { this.first = first; @@ -17,4 +17,12 @@ public class Pair { public T2 getSecond() { return second; } + + public void setSecond(T2 second) { + this.second = second; + } + + public void setFirst(T1 first) { + this.first = first; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java index 905236454..8e87f4009 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/AbstractVectorType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; import java.util.ArrayList; import java.util.List; import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; @@ -34,7 +34,7 @@ public abstract class AbstractVectorType implements WithSubValues { @Override public String toString() { - return Amf3Tools.amfToString(this); + return Amf3Exporter.amfToString(this); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java index 9c9813836..411d90003 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ArrayType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; import com.jpexs.decompiler.flash.amf.amf3.Pair; import com.jpexs.helpers.Helper; import java.util.ArrayList; @@ -27,7 +27,7 @@ public class ArrayType implements WithSubValues { @Override public String toString() { - return Amf3Tools.amfToString(this); + return Amf3Exporter.amfToString(this); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java index 2e0c57445..57699226c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ByteArrayType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; public class ByteArrayType { @@ -16,7 +16,7 @@ public class ByteArrayType { @Override public String toString() { - return Amf3Tools.amfToString(this); + return Amf3Exporter.amfToString(this); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java index 6458b9dff..50e73f215 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DateType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; import java.util.Date; public class DateType { @@ -21,7 +21,7 @@ public class DateType { @Override public String toString() { - return Amf3Tools.amfToString(this); + return Amf3Exporter.amfToString(this); } public Date toDate() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java index 6b2cc4cb2..bcae93132 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/DictionaryType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; import com.jpexs.decompiler.flash.amf.amf3.Pair; import java.util.ArrayList; import java.util.List; @@ -32,7 +32,7 @@ public class DictionaryType implements WithSubValues { @Override public String toString() { - return Amf3Tools.amfToString(this); + return Amf3Exporter.amfToString(this); } public boolean hasWeakKeys() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java index fd0deb2ff..4333a0f9c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/types/ObjectType.java @@ -1,6 +1,6 @@ package com.jpexs.decompiler.flash.amf.amf3.types; -import com.jpexs.decompiler.flash.amf.amf3.Amf3Tools; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; import com.jpexs.decompiler.flash.amf.amf3.Pair; import com.jpexs.decompiler.flash.amf.amf3.Traits; import com.jpexs.helpers.Helper; @@ -81,7 +81,7 @@ public class ObjectType implements WithSubValues { @Override public String toString() { - return Amf3Tools.amfToString(this); + return Amf3Exporter.amfToString(this); } public void setSerializedData(byte[] serializedData) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Tools.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/amf/amf3/Amf3Exporter.java similarity index 80% rename from libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Tools.java rename to libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/amf/amf3/Amf3Exporter.java index 899db6ae1..afe62a3c2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/amf/amf3/Amf3Tools.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/amf/amf3/Amf3Exporter.java @@ -1,5 +1,7 @@ -package com.jpexs.decompiler.flash.amf.amf3; +package com.jpexs.decompiler.flash.exporters.amf.amf3; +import com.jpexs.decompiler.flash.amf.amf3.Pair; +import com.jpexs.decompiler.flash.amf.amf3.WithSubValues; import com.jpexs.decompiler.flash.amf.amf3.types.ArrayType; import com.jpexs.decompiler.flash.amf.amf3.types.XmlType; import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; @@ -9,6 +11,7 @@ import com.jpexs.decompiler.flash.amf.amf3.types.ByteArrayType; import com.jpexs.decompiler.flash.amf.amf3.types.DateType; import com.jpexs.decompiler.flash.amf.amf3.types.DictionaryType; import com.jpexs.decompiler.flash.amf.amf3.types.BasicType; +import com.jpexs.decompiler.flash.ecma.EcmaScript; import com.jpexs.helpers.Helper; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -18,20 +21,20 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class Amf3Tools { +public class Amf3Exporter { /** * Converts AMF value to something human-readable. * - * @param obj + * @param amfValue * @return */ - public static String amfToString(Object obj) { + public static String amfToString(Object amfValue) { Map refCount = new HashMap<>(); List objectList = new ArrayList<>(); Map objectAlias = new HashMap<>(); - populateObjects(obj, refCount, objectList, objectAlias); - return amfToString(new ArrayList<>(), 0, obj, refCount, objectAlias); + populateObjects(amfValue, refCount, objectList, objectAlias); + return amfToString(new ArrayList<>(), 0, amfValue, refCount, objectAlias); } /** @@ -89,7 +92,7 @@ public class Amf3Tools { return "\"" + Helper.escapeActionScriptString((String) object) + "\""; } if (((List) Arrays.asList(Long.class, Double.class, Boolean.class)).contains(object.getClass())) { - return object.toString(); + return EcmaScript.toString(object); } if (object instanceof BasicType) { @@ -105,7 +108,7 @@ public class Amf3Tools { } processedObjects.add(object); - String addId = refCount > 1 ? indent(level + 1) + "\"id\": \"" + objectAlias.get(object) + "\"\r\n" : ""; + String addId = refCount > 1 ? indent(level + 1) + "\"id\": \"" + objectAlias.get(object) + "\",\r\n" : ""; if (object instanceof AbstractVectorType) { AbstractVectorType avt = (AbstractVectorType) object; @@ -134,7 +137,7 @@ public class Amf3Tools { if (serData == null) { ret.append(indent(level + 1)).append("\"serialized\": unknown\r\n"); } else { - ret.append(indent(level + 1)).append("\"serialized\": 0x").append(javax.xml.bind.DatatypeConverter.printHexBinary(serData)).append(",\r\n"); + ret.append(indent(level + 1)).append("\"serialized\": \"").append(javax.xml.bind.DatatypeConverter.printHexBinary(serData)).append("\",\r\n"); if (!ot.getSerializedMembers().isEmpty()) { ret.append(indent(level + 1)).append("\"unserializedMembers\": {\r\n"); for (int i = 0; i < ot.getSerializedMembers().size(); i++) { @@ -152,41 +155,44 @@ public class Amf3Tools { } } else { ret.append(indent(level + 1)).append("\"dynamic\": ").append(ot.isDynamic()).append(",\r\n"); - if (!ot.getSealedMembers().isEmpty()) { - ret.append(indent(level + 1)).append("\"sealedMembers\": {\r\n"); - for (int i = 0; i < ot.getSealedMembers().size(); i++) { - Pair member = ot.getSealedMembers().get(i); - ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 1, member.getSecond(), referenceCount, objectAlias)); - if (i < ot.getSealedMembers().size() - 1) { - ret.append(",\r\n"); - } else { - ret.append("\r\n"); - } + //if (!ot.getSealedMembers().isEmpty()) { + ret.append(indent(level + 1)).append("\"sealedMembers\": {\r\n"); + for (int i = 0; i < ot.getSealedMembers().size(); i++) { + Pair member = ot.getSealedMembers().get(i); + ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 1, member.getSecond(), referenceCount, objectAlias)); + if (i < ot.getSealedMembers().size() - 1) { + ret.append(",\r\n"); + } else { + ret.append("\r\n"); } - ret.append(indent(level + 1)).append("}"); - if (!ot.getDynamicMembers().isEmpty()) { + } + ret.append(indent(level + 1)).append("}"); + //if (!ot.getDynamicMembers().isEmpty()) { + ret.append(","); + //} + ret.append("\r\n"); + //} + //if (!ot.getDynamicMembers().isEmpty()) { + ret.append(indent(level + 1)).append("\"dynamicMembers\": {\r\n"); + for (int i = 0; i < ot.getDynamicMembers().size(); i++) { + Pair member = ot.getDynamicMembers().get(i); + ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)); + ret.append(":"); + ret.append(amfToString(processedObjects, level + 2, member.getSecond(), referenceCount, objectAlias)); + if (i < ot.getDynamicMembers().size() - 1) { ret.append(","); } ret.append("\r\n"); } - if (!ot.getDynamicMembers().isEmpty()) { - ret.append(indent(level + 1)).append("\"dynamicMembers\": {\r\n"); - for (int i = 0; i < ot.getDynamicMembers().size(); i++) { - Pair member = ot.getDynamicMembers().get(i); - ret.append(indent(level + 2)).append(amfToString(processedObjects, level + 2, member.getFirst(), referenceCount, objectAlias)).append(":").append(amfToString(processedObjects, level + 2, member.getSecond(), referenceCount, objectAlias)); - if (i < ot.getDynamicMembers().size() - 1) { - ret.append(","); - } - ret.append("\r\n"); - } - ret.append(indent(level + 1)).append("}\r\n"); - } + ret.append(indent(level + 1)).append("}\r\n"); + //} } ret.append(indent(level)).append("}"); } else if (object instanceof ArrayType) { ArrayType at = (ArrayType) object; ret.append("{\r\n"); ret.append(indent(level + 1)).append("\"type\": \"Array\",\r\n"); + ret.append(addId); ret.append(indent(level + 1)).append("\"denseValues\": ["); for (int i = 0; i < at.getDenseValues().size(); i++) { @@ -196,7 +202,7 @@ public class Amf3Tools { ret.append(amfToString(processedObjects, level + 2, at.getDenseValues().get(i), referenceCount, objectAlias)); } ret.append("],\r\n"); - ret.append(indent(level + 1)).append("\"associativeValues\": ["); + ret.append(indent(level + 1)).append("\"associativeValues\": {"); if (!at.getAssociativeValues().isEmpty()) { ret.append("\r\n"); } @@ -211,15 +217,15 @@ public class Amf3Tools { if (!at.getAssociativeValues().isEmpty()) { ret.append(indent(level + 1)); } - ret.append("]\r\n"); + ret.append("}\r\n"); ret.append(indent(level)).append("}"); } else if (object instanceof DictionaryType) { DictionaryType dt = (DictionaryType) object; ret.append("{\r\n"); ret.append(indent(level + 1)).append("\"type\": \"Dictionary\",\r\n"); ret.append(addId); - ret.append(indent(level + 1)).append("\"weakKeys\": " + dt.hasWeakKeys() + ",\r\n"); - ret.append(indent(level + 1)).append("\"value\": {\r\n"); + ret.append(indent(level + 1)).append("\"weakKeys\": ").append(dt.hasWeakKeys()).append(",\r\n"); + ret.append(indent(level + 1)).append("\"entries\": {\r\n"); for (int i = 0; i < dt.getPairs().size(); i++) { Pair pair = dt.getPairs().get(i); ret.append(indent(level + 1)).append(amfToString(processedObjects, level + 1, pair.getFirst(), referenceCount, objectAlias)).append(" : ").append(amfToString(processedObjects, level + 1, pair.getSecond(), referenceCount, objectAlias)); @@ -229,14 +235,14 @@ public class Amf3Tools { ret.append("\r\n"); } ret.append(indent(level + 1)).append("}\r\n"); - ret.append(indent(level) + "}"); + ret.append(indent(level)).append("}"); } else if (object instanceof ByteArrayType) { ByteArrayType ba = (ByteArrayType) object; byte data[] = ba.getData(); return "{\r\n" + indent(level + 1) + "\"type\": \"ByteArray\",\r\n" + addId - + indent(level + 1) + "\"value\": 0x" + javax.xml.bind.DatatypeConverter.printHexBinary(data) + "\r\n" + + indent(level + 1) + "\"value\": \"" + javax.xml.bind.DatatypeConverter.printHexBinary(data) + "\"\r\n" + indent(level) + "}"; } else if (object instanceof DateType) { DateType dt = (DateType) object; @@ -249,13 +255,13 @@ public class Amf3Tools { } else if (object instanceof XmlDocType) { return "{\r\n" - + indent(level + 1) + "\"type\": \"XML\",\r\n" + + indent(level + 1) + "\"type\": \"XMLDocument\",\r\n" + addId + indent(level + 1) + "\"value\": " + amfToString(processedObjects, level, ((XmlDocType) object).getData(), referenceCount, objectAlias) + "\r\n" + indent(level) + "}"; } else if (object instanceof XmlType) { return "{\r\n" - + indent(level + 1) + "\"type\": \"XMLDocument\",\r\n" + + indent(level + 1) + "\"type\": \"XML\",\r\n" + addId + indent(level + 1) + "\"value\": " + amfToString(processedObjects, level, ((XmlType) object).getData(), referenceCount, objectAlias) + "\r\n" + indent(level) + "}"; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Importer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Importer.java new file mode 100644 index 000000000..e21bd342f --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Importer.java @@ -0,0 +1,556 @@ +package com.jpexs.decompiler.flash.importers.amf.amf3; + +import com.jpexs.decompiler.flash.amf.amf3.Pair; +import com.jpexs.decompiler.flash.amf.amf3.Traits; +import com.jpexs.decompiler.flash.amf.amf3.types.ArrayType; +import com.jpexs.decompiler.flash.amf.amf3.types.BasicType; +import com.jpexs.decompiler.flash.amf.amf3.types.ByteArrayType; +import com.jpexs.decompiler.flash.amf.amf3.types.DateType; +import com.jpexs.decompiler.flash.amf.amf3.types.DictionaryType; +import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorDoubleType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorIntType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorObjectType; +import com.jpexs.decompiler.flash.amf.amf3.types.VectorUIntType; +import com.jpexs.decompiler.flash.amf.amf3.types.XmlDocType; +import com.jpexs.decompiler.flash.amf.amf3.types.XmlType; +import java.io.IOException; +import java.io.StringReader; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Amf3Importer { + + private Amf3Lexer lexer; + + private ParsedSymbol lex() throws IOException, Amf3ParseException { + ParsedSymbol ret = lexer.lex(); + return ret; + } + + private void pushback(ParsedSymbol s) { + lexer.pushback(s); + } + + private void expected(ParsedSymbol symb, int line, Object... expected) throws IOException, Amf3ParseException { + boolean found = false; + for (Object t : expected) { + if (symb.type == t) { + found = true; + } + if (symb.group == t) { + found = true; + } + } + if (!found) { + String expStr = ""; + boolean first = true; + for (Object e : expected) { + if (!first) { + expStr += " or "; + } + expStr += e; + first = false; + } + throw new Amf3ParseException("" + expStr + " expected but " + symb.type + " found", line); + } + } + + private ParsedSymbol expectedType(Object... type) throws IOException, Amf3ParseException { + ParsedSymbol symb = lex(); + expected(symb, lexer.yyline(), type); + return symb; + } + + private JsArray parseArray(Map objectTable) throws IOException, Amf3ParseException { + expectedType(SymbolType.BRACKET_OPEN); + List arrayVals = new ArrayList<>(); + ParsedSymbol s = lex(); + if (!s.isType(SymbolType.BRACKET_CLOSE)) { + pushback(s); + arrayVals.add(value(objectTable)); + s = lex(); + while (s.isType(SymbolType.COMMA)) { + arrayVals.add(value(objectTable)); + s = lex(); + } + pushback(s); + } + expectedType(SymbolType.BRACKET_CLOSE); + return new JsArray(arrayVals); + } + + private class JsArray { + + private List values = new ArrayList<>(); + + public JsArray() { + } + + public JsArray(List values) { + this.values = values; + } + + public void add(Object value) { + values.add(value); + } + + public List getValues() { + return values; + } + + } + + private class JsObject { + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("{"); + for (Pair p : values) { + sb.append(p.getFirst()).append(":").append("?").append(",\r\n"); + } + sb.append("}"); + return sb.toString(); + } + + private final List> values = new ArrayList<>(); + + public void add(Object key, Object value) { + values.add(new Pair<>(key, value)); + } + + public String getString(Object key) throws Amf3ParseException { + return (String) getRequired(key, "String"); + } + + public Boolean getBoolean(Object key) throws Amf3ParseException { + return (Boolean) getRequired(key, "Boolean"); + } + + public JsObject getJsObject(Object key) throws Amf3ParseException { + return (JsObject) getRequired(key, "JsObject"); + } + + public JsArray getJsArray(Object key) throws Amf3ParseException { + return (JsArray) getRequired(key, "JsArray"); + } + + public List getJsArrayOfObject(Object key) throws Amf3ParseException { + return getJsArray(key).getValues(); + } + + @SuppressWarnings("unchecked") + public List getJsArrayOfString(Object key) throws Amf3ParseException { + return (List) getJsArray(key, "String"); + } + + @SuppressWarnings("unchecked") + public List getJsArrayOfInt(Object key) throws Amf3ParseException { + return (List) getJsArray(key, "int"); + } + + @SuppressWarnings("unchecked") + + public List getJsArrayOfUint(Object key) throws Amf3ParseException { + return (List) getJsArray(key, "uint"); + } + + @SuppressWarnings("unchecked") + public List getJsArrayOfNumber(Object key) throws Amf3ParseException { + return (List) getJsArray(key, "Number"); + } + + public List getJsArray(Object key, String valueType) throws Amf3ParseException { + JsArray jsArr = (JsArray) getRequired(key, "JsArray"); + switch (valueType) { + case "String": + List stringList = new ArrayList<>(); + for (Object v : jsArr.getValues()) { + String sv = null; + if (v instanceof String) { + sv = (String) v; + } else { + throw new Amf3ParseException("Not String: " + v, 0); + } + stringList.add(sv); + } + return stringList; + case "int": + case "uint": + List longList = new ArrayList<>(); + for (Object v : jsArr.getValues()) { + Long lv = null; + if (v instanceof Long) { + lv = (Long) v; + } else { + throw new Amf3ParseException("Not an Integer value: " + v, 0); + } + if (valueType.equals("uint") && lv < 0) { + throw new Amf3ParseException("Not an Unsigned Integer value: " + v, 0); + } + longList.add(lv); + } + return longList; + case "Number": + List doubleList = new ArrayList<>(); + for (Object v : jsArr.getValues()) { + Double cv = null; + if (v instanceof Long) { + cv = (double) (long) (Long) v; + } else if (v instanceof Double) { + cv = (Double) v; + } else { + throw new Amf3ParseException("Not a Number: " + v, 0); + } + doubleList.add(cv); + } + return doubleList; + default: + throw new Amf3ParseException("Unsupported array value type: " + valueType, 0); + } + } + + public Long getLong(Object key) throws Amf3ParseException { + return (Long) getRequired(key, "Long"); + } + + public Object getRequired(Object key, String requiredType) throws Amf3ParseException { + if (!containsKey(key)) { + throw new Amf3ParseException("\"" + key + "\" is missing", 0); + } + Object val = get(key); + boolean typeMatches = true; + if (requiredType != null) { + switch (requiredType) { + case "String": + typeMatches = val instanceof String; + break; + case "Long": + typeMatches = val instanceof Long; + break; + case "JsObject": + typeMatches = val instanceof JsObject; + break; + case "JsArray": + typeMatches = val instanceof JsArray; + break; + case "Boolean": + typeMatches = val instanceof Boolean; + break; + } + } + if (!typeMatches) { + throw new Amf3ParseException("\"" + key + "\" value must be of type " + requiredType, 0); + } + return val; + } + + public boolean containsKey(Object key) { + for (Pair p : values) { + if (p.getFirst().equals(key)) { + return true; + } + } + return false; + } + + public Object get(Object key) { + for (Pair p : values) { + if (p.getFirst().equals(key)) { + return p.getSecond(); + } + } + return null; + } + + public void resolve(Object key, Map objectTable, boolean allowTypedObject) throws Amf3ParseException { + for (Pair p : values) { + if (p.getFirst().equals(key)) { + Object resolved = resolveObjects(p.getSecond(), objectTable, allowTypedObject); + p.setSecond(resolved); + return; + } + } + } + + public List stringKeys() { + List ret = new ArrayList<>(); + for (Pair p : values) { + if (p.getFirst() instanceof String) { + ret.add((String) p.getFirst()); + } + } + return ret; + } + + public List> getAll() { + return values; + } + + public List> getStringMapped() { + List> ret = new ArrayList<>(); + for (Pair p : values) { + if (p.getFirst() instanceof String) { + String keyStr = (String) p.getFirst(); + ret.add(new Pair<>(keyStr, p.getSecond())); + } + } + return ret; + } + + public Map getStringMap() { + Map ret = new HashMap<>(); + for (Pair p : values) { + if (p.getFirst() instanceof String) { + String keyStr = (String) p.getFirst(); + ret.put(keyStr, values); + } + } + return ret; + } + } + + private JsObject parseObject(Map objectTable) throws IOException, Amf3ParseException { + JsObject ret = new JsObject(); + + expectedType(SymbolType.CURLY_OPEN); + ParsedSymbol s = lex(); + if (!s.isType(SymbolType.CURLY_CLOSE)) { + pushback(s); + do { + Object key = value(objectTable); + expectedType(SymbolType.COLON); + Object value = value(objectTable); + ret.add(key, value); + if ("id".equals(key)) { + if (!(value instanceof String)) { + throw new Amf3ParseException("id must be string value", lexer.yyline()); + } + objectTable.put((String) value, BasicType.UNDEFINED); + } + s = lex(); + } while (s.isType(SymbolType.COMMA)); + } + pushback(s); + expectedType(SymbolType.CURLY_CLOSE); + return ret; + } + + private Object resolveObjects(Object object, Map objectTable, boolean allowTypedObject) throws Amf3ParseException { + Object resultObject = object; + if (object instanceof JsArray) { + JsArray jsa = (JsArray) object; + JsArray ret = new JsArray(); + for (int i = 0; i < jsa.values.size(); i++) { + ret.values.add(resolveObjects(jsa.values.get(i), objectTable, true)); + } + resultObject = ret; + } else if (object instanceof JsObject) { + if (allowTypedObject) { + JsObject typedObject = (JsObject) object; + if (typedObject.containsKey("type")) { + String typeStr = typedObject.getString("type"); + String id = typedObject.containsKey("id") ? typedObject.getString("id") : null; + switch (typeStr) { + case "Date": + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS"); + String dateStr = typedObject.getString("value"); + try { + resultObject = new DateType((double) sdf.parse(dateStr).getTime()); + } catch (ParseException ex) { + throw new Amf3ParseException("Invalid date format: " + dateStr, lexer.yyline()); + } + break; + case "XML": + resultObject = new XmlType(typedObject.getString("value")); + break; + case "XMLDocument": + resultObject = new XmlDocType(typedObject.getString("value")); + break; + case "Object": + String className = typedObject.getString("className"); + if (typedObject.containsKey("serialized")) { + //TODO + } else { + boolean dynamic = typedObject.getBoolean("dynamic"); + typedObject.resolve("sealedMembers", objectTable, false); + JsObject jsoSealed = typedObject.getJsObject("sealedMembers"); + + List> sealedMembers = jsoSealed.getStringMapped(); + typedObject.resolve("dynamicMembers", objectTable, false); + List> dynamicMembers = typedObject.getJsObject("dynamicMembers").getStringMapped(); + + List sealedMemberNames = new ArrayList<>(jsoSealed.stringKeys()); + resultObject = new ObjectType(new Traits(className, dynamic, sealedMemberNames), sealedMembers, dynamicMembers); + } + break; + case "Array": + typedObject.resolve("denseValues", objectTable, false); + List denseValues = typedObject.getJsArray("denseValues").getValues(); + typedObject.resolve("associativeValues", objectTable, false); + JsObject resolvedArr = typedObject.getJsObject("associativeValues"); + List> associativeValues = resolvedArr.getStringMapped(); + resultObject = new ArrayType(denseValues, associativeValues); + break; + + case "Vector": + boolean fixed = typedObject.getBoolean("fixed"); + String subtype = typedObject.getString("subtype"); + typedObject.resolve("values", objectTable, false); + switch (subtype) { + case "int": + resultObject = new VectorIntType(fixed, typedObject.getJsArrayOfInt("values")); + break; + case "uint": + resultObject = new VectorUIntType(fixed, typedObject.getJsArrayOfUint("values")); + break; + case "Number": + resultObject = new VectorDoubleType(fixed, typedObject.getJsArrayOfNumber("values")); + break; + default: + resultObject = new VectorObjectType(fixed, subtype, typedObject.getJsArrayOfObject("values")); + break; + } + break; + case "ByteArray": + try { + resultObject = new ByteArrayType(javax.xml.bind.DatatypeConverter.parseHexBinary(typedObject.getString("value"))); + } catch (IllegalArgumentException iex) { + throw new Amf3ParseException("Invalid hex byte sequence", lexer.yyline()); + } + break; + case "Dictionary": + boolean weakKeys = typedObject.getBoolean("weakKeys"); + typedObject.resolve("entries", objectTable, false); + List> entries = typedObject.getJsObject("entries").getAll(); + resultObject = new DictionaryType(weakKeys, entries); + break; + default: + throw new Amf3ParseException("Unknown object type: " + typeStr, lexer.yyline()); + } + if (id != null) { + objectTable.put(id, resultObject); + } + } + } else { //not allowTypeObject + JsObject jsObject = (JsObject) object; + for (Pair p : jsObject.getAll()) { + p.setFirst(resolveObjects(p.getFirst(), objectTable, true)); + p.setSecond(resolveObjects(p.getSecond(), objectTable, true)); + } + resultObject = jsObject; + } + } + return resultObject; + } + + private Object value(Map objectTable) throws IOException, Amf3ParseException { + ParsedSymbol s = lex(); + switch (s.type) { + case CURLY_OPEN: + pushback(s); + return parseObject(objectTable); + case BRACKET_OPEN: + pushback(s); + return parseArray(objectTable); + case STRING: + case DOUBLE: + case INTEGER: + return s.value; + case UNDEFINED: + return BasicType.UNDEFINED; + case NULL: + return BasicType.NULL; + case UNKNOWN: + return BasicType.UNKNOWN; + case TRUE: + return Boolean.TRUE; + case FALSE: + return Boolean.FALSE; + case REFERENCE: + String referencedId = (String) s.value; + return new ReferencedObjectType(referencedId); + default: + throw new Amf3ParseException("Unexpected symbol: " + s, lexer.yyline()); + } + } + + /** + * Deeply replace all ReferencedObjectType with the correct value + * + * @param object + * @param objectsTable + * @return + */ + private Object replaceReferences(Object object, Map objectsTable) throws Amf3ParseException { + if (object instanceof ReferencedObjectType) { + String key = ((ReferencedObjectType) object).key; + if (!objectsTable.containsKey(key)) { + throw new Amf3ParseException("Reference to undefined object: #" + key, 0); + } + return objectsTable.get(key); + } else if (object instanceof ObjectType) { + ObjectType ot = (ObjectType) object; + for (Pair p : ot.getSealedMembers()) { + p.setSecond(replaceReferences(p.getSecond(), objectsTable)); + } + for (Pair p : ot.getDynamicMembers()) { + p.setSecond(replaceReferences(p.getSecond(), objectsTable)); + } + } else if (object instanceof ArrayType) { + ArrayType at = (ArrayType) object; + for (Pair p : at.getAssociativeValues()) { + p.setSecond(replaceReferences(p.getSecond(), objectsTable)); + } + for (int i = 0; i < at.getDenseValues().size(); i++) { + at.getDenseValues().set(i, replaceReferences(at.getDenseValues().get(i), objectsTable)); + } + } else if (object instanceof VectorObjectType) { + VectorObjectType vot = (VectorObjectType) object; + for (int i = 0; i < vot.getValues().size(); i++) { + vot.getValues().set(i, replaceReferences(vot.getValues().get(i), objectsTable)); + } + } else if (object instanceof DictionaryType) { + DictionaryType dt = (DictionaryType) object; + for (Pair p : dt.getPairs()) { + p.setFirst(replaceReferences(p.getFirst(), objectsTable)); + p.setSecond(replaceReferences(p.getSecond(), objectsTable)); + } + } + + return object; + } + + private class ReferencedObjectType { + + private final String key; + + public ReferencedObjectType(String key) { + this.key = key; + } + + public String getKey() { + return key; + } + + @Override + public String toString() { + return "#" + key; + } + } + + public Object stringToAmf(String val) throws IOException, Amf3ParseException { + lexer = new Amf3Lexer(new StringReader(val)); + Map objectsTable = new HashMap<>(); + List references = new ArrayList<>(); + Object result = value(objectsTable); + Object resultResolved = resolveObjects(result, objectsTable, true); + Object resultNoRef = replaceReferences(resultResolved, objectsTable); + return resultNoRef; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Lexer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Lexer.java new file mode 100644 index 000000000..7052c61ef --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3Lexer.java @@ -0,0 +1,1033 @@ +/* The following code was generated by JFlex 1.6.0 */ + +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.amf.amf3; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + + +/** + * This class is a scanner generated by + * JFlex 1.6.0 + * from the specification file D:/Dropbox/Programovani/JavaSE/FFDec/libsrc/ffdec_lib/lexers/amf3.flex + */ +public final class Amf3Lexer { + + /** This character denotes the end of file */ + public static final int YYEOF = -1; + + /** initial size of the lookahead buffer */ + private static final int ZZ_BUFFERSIZE = 16384; + + /** lexical states */ + public static final int YYINITIAL = 0; + public static final int STRING = 2; + public static final int CHARLITERAL = 4; + + /** + * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l + * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l + * at the beginning of a line + * l is of the form l = 2*k, k a non negative integer + */ + private static final int ZZ_LEXSTATE[] = { + 0, 0, 1, 1, 2, 2 + }; + + /** + * Translates characters to character classes + */ + private static final String ZZ_CMAP_PACKED = + "\11\7\1\3\1\2\1\54\1\55\1\1\16\7\4\0\1\3\1\0"+ + "\1\50\1\10\1\6\2\0\1\52\2\0\1\5\1\22\1\46\1\12"+ + "\1\20\1\4\1\11\3\16\4\17\2\13\1\47\6\0\4\15\1\21"+ + "\1\15\21\6\1\14\2\6\1\44\1\23\1\45\1\0\1\6\1\0"+ + "\1\33\1\51\1\15\1\27\1\30\1\31\2\6\1\32\1\6\1\37"+ + "\1\26\1\6\1\24\1\40\2\6\1\36\1\34\1\35\1\25\1\6"+ + "\1\41\1\53\2\6\1\42\1\0\1\43\1\0\6\7\1\56\32\7"+ + "\2\0\4\6\4\0\1\6\2\0\1\7\7\0\1\6\4\0\1\6"+ + "\5\0\27\6\1\0\37\6\1\0\u01ca\6\4\0\14\6\16\0\5\6"+ + "\7\0\1\6\1\0\1\6\21\0\160\7\5\6\1\0\2\6\2\0"+ + "\4\6\10\0\1\6\1\0\3\6\1\0\1\6\1\0\24\6\1\0"+ + "\123\6\1\0\213\6\1\0\5\7\2\0\236\6\11\0\46\6\2\0"+ + "\1\6\7\0\47\6\7\0\1\6\1\0\55\7\1\0\1\7\1\0"+ + "\2\7\1\0\2\7\1\0\1\7\10\0\33\6\5\0\3\6\15\0"+ + "\5\7\6\0\1\6\4\0\13\7\5\0\53\6\37\7\4\0\2\6"+ + "\1\7\143\6\1\0\1\6\10\7\1\0\6\7\2\6\2\7\1\0"+ + "\4\7\2\6\12\7\3\6\2\0\1\6\17\0\1\7\1\6\1\7"+ + "\36\6\33\7\2\0\131\6\13\7\1\6\16\0\12\7\41\6\11\7"+ + "\2\6\4\0\1\6\5\0\26\6\4\7\1\6\11\7\1\6\3\7"+ + "\1\6\5\7\22\0\31\6\3\7\104\0\1\6\1\0\13\6\67\0"+ + "\33\7\1\0\4\7\66\6\3\7\1\6\22\7\1\6\7\7\12\6"+ + "\2\7\2\0\12\7\1\0\7\6\1\0\7\6\1\0\3\7\1\0"+ + "\10\6\2\0\2\6\2\0\26\6\1\0\7\6\1\0\1\6\3\0"+ + "\4\6\2\0\1\7\1\6\7\7\2\0\2\7\2\0\3\7\1\6"+ + "\10\0\1\7\4\0\2\6\1\0\3\6\2\7\2\0\12\7\4\6"+ + "\7\0\1\6\5\0\3\7\1\0\6\6\4\0\2\6\2\0\26\6"+ + "\1\0\7\6\1\0\2\6\1\0\2\6\1\0\2\6\2\0\1\7"+ + "\1\0\5\7\4\0\2\7\2\0\3\7\3\0\1\7\7\0\4\6"+ + "\1\0\1\6\7\0\14\7\3\6\1\7\13\0\3\7\1\0\11\6"+ + "\1\0\3\6\1\0\26\6\1\0\7\6\1\0\2\6\1\0\5\6"+ + "\2\0\1\7\1\6\10\7\1\0\3\7\1\0\3\7\2\0\1\6"+ + "\17\0\2\6\2\7\2\0\12\7\1\0\1\6\17\0\3\7\1\0"+ + "\10\6\2\0\2\6\2\0\26\6\1\0\7\6\1\0\2\6\1\0"+ + "\5\6\2\0\1\7\1\6\7\7\2\0\2\7\2\0\3\7\10\0"+ + "\2\7\4\0\2\6\1\0\3\6\2\7\2\0\12\7\1\0\1\6"+ + "\20\0\1\7\1\6\1\0\6\6\3\0\3\6\1\0\4\6\3\0"+ + "\2\6\1\0\1\6\1\0\2\6\3\0\2\6\3\0\3\6\3\0"+ + "\14\6\4\0\5\7\3\0\3\7\1\0\4\7\2\0\1\6\6\0"+ + "\1\7\16\0\12\7\11\0\1\6\7\0\3\7\1\0\10\6\1\0"+ + "\3\6\1\0\27\6\1\0\12\6\1\0\5\6\3\0\1\6\7\7"+ + "\1\0\3\7\1\0\4\7\7\0\2\7\1\0\2\6\6\0\2\6"+ + "\2\7\2\0\12\7\22\0\2\7\1\0\10\6\1\0\3\6\1\0"+ + "\27\6\1\0\12\6\1\0\5\6\2\0\1\7\1\6\7\7\1\0"+ + "\3\7\1\0\4\7\7\0\2\7\7\0\1\6\1\0\2\6\2\7"+ + "\2\0\12\7\1\0\2\6\17\0\2\7\1\0\10\6\1\0\3\6"+ + "\1\0\51\6\2\0\1\6\7\7\1\0\3\7\1\0\4\7\1\6"+ + "\10\0\1\7\10\0\2\6\2\7\2\0\12\7\12\0\6\6\2\0"+ + "\2\7\1\0\22\6\3\0\30\6\1\0\11\6\1\0\1\6\2\0"+ + "\7\6\3\0\1\7\4\0\6\7\1\0\1\7\1\0\10\7\22\0"+ + "\2\7\15\0\60\6\1\7\2\6\7\7\4\0\10\6\10\7\1\0"+ + "\12\7\47\0\2\6\1\0\1\6\2\0\2\6\1\0\1\6\2\0"+ + "\1\6\6\0\4\6\1\0\7\6\1\0\3\6\1\0\1\6\1\0"+ + "\1\6\2\0\2\6\1\0\4\6\1\7\2\6\6\7\1\0\2\7"+ + "\1\6\2\0\5\6\1\0\1\6\1\0\6\7\2\0\12\7\2\0"+ + "\4\6\40\0\1\6\27\0\2\7\6\0\12\7\13\0\1\7\1\0"+ + "\1\7\1\0\1\7\4\0\2\7\10\6\1\0\44\6\4\0\24\7"+ + "\1\0\2\7\5\6\13\7\1\0\44\7\11\0\1\7\71\0\53\6"+ + "\24\7\1\6\12\7\6\0\6\6\4\7\4\6\3\7\1\6\3\7"+ + "\2\6\7\7\3\6\4\7\15\6\14\7\1\6\17\7\2\0\46\6"+ + "\1\0\1\6\5\0\1\6\2\0\53\6\1\0\u014d\6\1\0\4\6"+ + "\2\0\7\6\1\0\1\6\1\0\4\6\2\0\51\6\1\0\4\6"+ + "\2\0\41\6\1\0\4\6\2\0\7\6\1\0\1\6\1\0\4\6"+ + "\2\0\17\6\1\0\71\6\1\0\4\6\2\0\103\6\2\0\3\7"+ + "\40\0\20\6\20\0\125\6\14\0\u026c\6\2\0\21\6\1\0\32\6"+ + "\5\0\113\6\3\0\3\6\17\0\15\6\1\0\4\6\3\7\13\0"+ + "\22\6\3\7\13\0\22\6\2\7\14\0\15\6\1\0\3\6\1\0"+ + "\2\7\14\0\64\6\40\7\3\0\1\6\3\0\2\6\1\7\2\0"+ + "\12\7\41\0\3\7\2\0\12\7\6\0\130\6\10\0\51\6\1\7"+ + "\1\6\5\0\106\6\12\0\35\6\3\0\14\7\4\0\14\7\12\0"+ + "\12\7\36\6\2\0\5\6\13\0\54\6\4\0\21\7\7\6\2\7"+ + "\6\0\12\7\46\0\27\6\5\7\4\0\65\6\12\7\1\0\35\7"+ + "\2\0\13\7\6\0\12\7\15\0\1\6\130\0\5\7\57\6\21\7"+ + "\7\6\4\0\12\7\21\0\11\7\14\0\3\7\36\6\15\7\2\6"+ + "\12\7\54\6\16\7\14\0\44\6\24\7\10\0\12\7\3\0\3\6"+ + "\12\7\44\6\122\0\3\7\1\0\25\7\4\6\1\7\4\6\3\7"+ + "\2\6\11\0\300\6\47\7\25\0\4\7\u0116\6\2\0\6\6\2\0"+ + "\46\6\2\0\6\6\2\0\10\6\1\0\1\6\1\0\1\6\1\0"+ + "\1\6\1\0\37\6\2\0\65\6\1\0\7\6\1\0\1\6\3\0"+ + "\3\6\1\0\7\6\3\0\4\6\2\0\6\6\4\0\15\6\5\0"+ + "\3\6\1\0\7\6\16\0\5\7\30\0\1\54\1\54\5\7\20\0"+ + "\2\6\23\0\1\6\13\0\5\7\5\0\6\7\1\0\1\6\15\0"+ + "\1\6\20\0\15\6\3\0\33\6\25\0\15\7\4\0\1\7\3\0"+ + "\14\7\21\0\1\6\4\0\1\6\2\0\12\6\1\0\1\6\3\0"+ + "\5\6\6\0\1\6\1\0\1\6\1\0\1\6\1\0\4\6\1\0"+ + "\13\6\2\0\4\6\5\0\5\6\4\0\1\6\21\0\51\6\u0a77\0"+ + "\57\6\1\0\57\6\1\0\205\6\6\0\4\6\3\7\2\6\14\0"+ + "\46\6\1\0\1\6\5\0\1\6\2\0\70\6\7\0\1\6\17\0"+ + "\1\7\27\6\11\0\7\6\1\0\7\6\1\0\7\6\1\0\7\6"+ + "\1\0\7\6\1\0\7\6\1\0\7\6\1\0\7\6\1\0\40\7"+ + "\57\0\1\6\u01d5\0\3\6\31\0\11\6\6\7\1\0\5\6\2\0"+ + "\5\6\4\0\126\6\2\0\2\7\2\0\3\6\1\0\132\6\1\0"+ + "\4\6\5\0\51\6\3\0\136\6\21\0\33\6\65\0\20\6\u0200\0"+ + "\u19b6\6\112\0\u51cd\6\63\0\u048d\6\103\0\56\6\2\0\u010d\6\3\0"+ + "\20\6\12\7\2\6\24\0\57\6\1\7\4\0\12\7\1\0\31\6"+ + "\7\0\1\7\120\6\2\7\45\0\11\6\2\0\147\6\2\0\4\6"+ + "\1\0\4\6\14\0\13\6\115\0\12\6\1\7\3\6\1\7\4\6"+ + "\1\7\27\6\5\7\20\0\1\6\7\0\64\6\14\0\2\7\62\6"+ + "\21\7\13\0\12\7\6\0\22\7\6\6\3\0\1\6\4\0\12\7"+ + "\34\6\10\7\2\0\27\6\15\7\14\0\35\6\3\0\4\7\57\6"+ + "\16\7\16\0\1\6\12\7\46\0\51\6\16\7\11\0\3\6\1\7"+ + "\10\6\2\7\2\0\12\7\6\0\27\6\3\0\1\6\1\7\4\0"+ + "\60\6\1\7\1\6\3\7\2\6\2\7\5\6\2\7\1\6\1\7"+ + "\1\6\30\0\3\6\2\0\13\6\5\7\2\0\3\6\2\7\12\0"+ + "\6\6\2\0\6\6\2\0\6\6\11\0\7\6\1\0\7\6\221\0"+ + "\43\6\10\7\1\0\2\7\2\0\12\7\6\0\u2ba4\6\14\0\27\6"+ + "\4\0\61\6\u2104\0\u016e\6\2\0\152\6\46\0\7\6\14\0\5\6"+ + "\5\0\1\6\1\7\12\6\1\0\15\6\1\0\5\6\1\0\1\6"+ + "\1\0\2\6\1\0\2\6\1\0\154\6\41\0\u016b\6\22\0\100\6"+ + "\2\0\66\6\50\0\15\6\3\0\20\7\20\0\7\7\14\0\2\6"+ + "\30\0\3\6\31\0\1\6\6\0\5\6\1\0\207\6\2\0\1\7"+ + "\4\0\1\6\13\0\12\7\7\0\32\6\4\0\1\6\1\0\32\6"+ + "\13\0\131\6\3\0\6\6\2\0\6\6\2\0\6\6\2\0\3\6"+ + "\3\0\2\6\3\0\2\6\22\0\3\7\4\0\14\6\1\0\32\6"+ + "\1\0\23\6\1\0\2\6\1\0\17\6\2\0\16\6\42\0\173\6"+ + "\105\0\65\6\210\0\1\7\202\0\35\6\3\0\61\6\57\0\37\6"+ + "\21\0\33\6\65\0\36\6\2\0\44\6\4\0\10\6\1\0\5\6"+ + "\52\0\236\6\2\0\12\7\u0356\0\6\6\2\0\1\6\1\0\54\6"+ + "\1\0\2\6\3\0\1\6\2\0\27\6\252\0\26\6\12\0\32\6"+ + "\106\0\70\6\6\0\2\6\100\0\1\6\3\7\1\0\2\7\5\0"+ + "\4\7\4\6\1\0\3\6\1\0\33\6\4\0\3\7\4\0\1\7"+ + "\40\0\35\6\203\0\66\6\12\0\26\6\12\0\23\6\215\0\111\6"+ + "\u03b7\0\3\7\65\6\17\7\37\0\12\7\20\0\3\7\55\6\13\7"+ + "\2\0\1\7\22\0\31\6\7\0\12\7\6\0\3\7\44\6\16\7"+ + "\1\0\12\7\100\0\3\7\60\6\16\7\4\6\13\0\12\7\u04a6\0"+ + "\53\6\15\7\10\0\12\7\u0936\0\u036f\6\221\0\143\6\u0b9d\0\u042f\6"+ + "\u33d1\0\u0239\6\u04c7\0\105\6\13\0\1\6\56\7\20\0\4\7\15\6"+ + "\u4060\0\2\6\u2163\0\5\7\3\0\26\7\2\0\7\7\36\0\4\7"+ + "\224\0\3\7\u01bb\0\125\6\1\0\107\6\1\0\2\6\2\0\1\6"+ + "\2\0\2\6\2\0\4\6\1\0\14\6\1\0\1\6\1\0\7\6"+ + "\1\0\101\6\1\0\4\6\2\0\10\6\1\0\7\6\1\0\34\6"+ + "\1\0\4\6\1\0\5\6\1\0\1\6\3\0\7\6\1\0\u0154\6"+ + "\2\0\31\6\1\0\31\6\1\0\37\6\1\0\31\6\1\0\37\6"+ + "\1\0\31\6\1\0\37\6\1\0\31\6\1\0\37\6\1\0\31\6"+ + "\1\0\10\6\2\0\62\7\u1600\0\4\6\1\0\33\6\1\0\2\6"+ + "\1\0\1\6\2\0\1\6\1\0\12\6\1\0\4\6\1\0\1\6"+ + "\1\0\1\6\6\0\1\6\4\0\1\6\1\0\1\6\1\0\1\6"+ + "\1\0\3\6\1\0\2\6\1\0\1\6\2\0\1\6\1\0\1\6"+ + "\1\0\1\6\1\0\1\6\1\0\1\6\1\0\2\6\1\0\1\6"+ + "\2\0\4\6\1\0\7\6\1\0\4\6\1\0\4\6\1\0\1\6"+ + "\1\0\12\6\1\0\21\6\5\0\3\6\1\0\5\6\1\0\21\6"+ + "\u1144\0\ua6d7\6\51\0\u1035\6\13\0\336\6\u3fe2\0\u021e\6\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\u05ee\0"+ + "\1\7\36\0\140\7\200\0\360\7\uffff\0\uffff\0\ufe12\0"; + + /** + * Translates characters to character classes + */ + private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); + + /** + * Translates DFA states to action switch labels. + */ + private static final int [] ZZ_ACTION = zzUnpackAction(); + + private static final String ZZ_ACTION_PACKED_0 = + "\3\0\1\1\2\2\1\3\1\1\1\4\1\1\1\5"+ + "\1\1\1\5\1\1\4\4\1\6\1\7\1\10\1\11"+ + "\1\12\1\13\1\14\1\15\2\16\1\1\1\17\1\3"+ + "\1\0\1\20\1\21\1\22\1\0\2\21\1\22\2\0"+ + "\4\4\2\23\1\24\1\25\1\23\1\26\1\27\1\30"+ + "\1\31\1\32\1\33\1\23\2\3\2\0\2\34\1\21"+ + "\1\22\1\0\5\4\4\0\1\34\1\21\1\35\3\4"+ + "\1\36\1\37\1\0\1\40\1\34\1\21\2\4\1\41"+ + "\1\34\1\21\2\4\1\34\1\21\1\4\1\42\1\34"+ + "\1\21\1\4\1\34\1\21\1\43\1\34\7\21"; + + private static int [] zzUnpackAction() { + int [] result = new int[112]; + int offset = 0; + offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAction(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + + /** + * Translates a state to a row index in the transition table + */ + private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); + + private static final String ZZ_ROWMAP_PACKED_0 = + "\0\0\0\57\0\136\0\215\0\274\0\215\0\353\0\u011a"+ + "\0\u0149\0\u0178\0\u01a7\0\u01d6\0\u0205\0\u0234\0\u0263\0\u0292"+ + "\0\u02c1\0\u02f0\0\215\0\215\0\215\0\215\0\215\0\215"+ + "\0\215\0\u031f\0\u034e\0\215\0\u037d\0\215\0\u03ac\0\u03db"+ + "\0\u040a\0\u0439\0\u0468\0\u0497\0\u04c6\0\u04f5\0\u0524\0\u0553"+ + "\0\u0234\0\u0582\0\u05b1\0\u05e0\0\u060f\0\215\0\u063e\0\215"+ + "\0\215\0\u066d\0\215\0\215\0\215\0\215\0\215\0\215"+ + "\0\u069c\0\u06cb\0\215\0\u06fa\0\u0729\0\u0497\0\u0758\0\u0787"+ + "\0\u07b6\0\u07b6\0\u07e5\0\u0814\0\u0843\0\u0872\0\u08a1\0\u08d0"+ + "\0\u08ff\0\u092e\0\u095d\0\u098c\0\u09bb\0\u0149\0\u09ea\0\u0a19"+ + "\0\u0a48\0\u0149\0\215\0\u069c\0\215\0\u0a77\0\u0aa6\0\u0ad5"+ + "\0\u0b04\0\u0149\0\u0b33\0\u0b62\0\u0b91\0\u0bc0\0\u0bef\0\u0c1e"+ + "\0\u0c4d\0\u0149\0\u0c7c\0\u0cab\0\u0cda\0\u0d09\0\u0d38\0\u0149"+ + "\0\215\0\u0d67\0\u0d96\0\u0dc5\0\u0df4\0\u0e23\0\u0e52\0\u0468"; + + private static int [] zzUnpackRowMap() { + int [] result = new int[112]; + int offset = 0; + offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackRowMap(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int high = packed.charAt(i++) << 16; + result[j++] = high | packed.charAt(i++); + } + return j; + } + + /** + * The transition table of the DFA + */ + private static final int [] ZZ_TRANS = zzUnpackTrans(); + + private static final String ZZ_TRANS_PACKED_0 = + "\1\4\1\5\1\6\1\7\1\10\1\4\1\11\1\4"+ + "\1\12\1\13\1\14\1\15\2\11\2\15\1\16\1\11"+ + "\2\4\1\17\1\20\3\11\1\21\3\11\1\22\4\11"+ + "\1\23\1\24\1\25\1\26\1\27\1\30\1\31\1\11"+ + "\1\4\1\11\1\4\1\7\1\4\1\32\1\33\1\34"+ + "\20\32\1\35\24\32\1\36\6\32\57\4\61\0\1\6"+ + "\57\0\1\7\51\0\1\7\5\0\1\37\1\40\57\0"+ + "\2\11\1\0\1\11\1\0\5\11\1\0\1\11\2\0"+ + "\16\11\7\0\1\11\1\0\1\11\2\0\1\11\6\0"+ + "\1\41\5\0\2\41\3\0\1\41\2\0\16\41\7\0"+ + "\1\41\1\0\1\41\14\0\1\42\1\0\1\43\1\44"+ + "\1\0\1\45\1\46\1\47\1\50\6\0\1\50\22\0"+ + "\1\44\14\0\1\43\1\0\1\15\2\0\2\15\1\51"+ + "\47\0\1\15\1\0\1\15\2\0\2\15\1\47\1\50"+ + "\6\0\1\50\37\0\1\47\1\0\1\47\2\0\2\47"+ + "\45\0\2\11\1\0\1\11\1\0\5\11\1\0\1\11"+ + "\2\0\1\11\1\52\14\11\7\0\1\11\1\0\1\11"+ + "\2\0\1\11\6\0\2\11\1\0\1\11\1\0\5\11"+ + "\1\0\1\11\2\0\1\53\15\11\7\0\1\11\1\0"+ + "\1\11\2\0\1\11\6\0\2\11\1\0\1\11\1\0"+ + "\5\11\1\0\1\11\2\0\7\11\1\54\6\11\7\0"+ + "\1\11\1\0\1\11\2\0\1\11\6\0\2\11\1\0"+ + "\1\11\1\0\5\11\1\0\1\11\2\0\12\11\1\55"+ + "\3\11\7\0\1\11\1\0\1\11\2\0\1\11\1\32"+ + "\2\0\20\32\1\0\24\32\1\0\6\32\2\0\1\34"+ + "\54\0\1\56\2\0\6\56\1\57\4\56\2\57\3\56"+ + "\1\60\1\61\1\62\3\56\1\63\3\56\1\64\1\65"+ + "\11\56\1\66\1\67\1\70\1\71\3\0\1\37\1\72"+ + "\1\73\54\37\5\74\1\75\51\74\6\0\2\41\1\0"+ + "\1\41\1\0\5\41\1\0\1\41\2\0\16\41\7\0"+ + "\1\41\1\0\1\41\2\0\1\41\11\0\1\42\1\0"+ + "\1\43\2\0\1\45\1\46\1\47\1\50\6\0\1\50"+ + "\37\0\1\43\1\0\1\43\2\0\2\43\1\47\1\50"+ + "\6\0\1\50\37\0\1\76\1\0\1\77\1\0\3\77"+ + "\1\0\1\77\5\0\3\77\1\0\1\77\15\0\1\77"+ + "\16\0\1\46\1\0\1\43\2\0\2\46\1\47\1\50"+ + "\6\0\1\50\37\0\1\100\1\0\1\43\2\0\2\100"+ + "\1\47\1\50\6\0\1\50\37\0\1\47\1\0\1\47"+ + "\2\0\2\47\1\0\1\50\6\0\1\50\37\0\1\101"+ + "\1\102\1\101\2\0\2\101\2\0\1\102\42\0\2\11"+ + "\1\0\1\11\1\0\5\11\1\0\1\11\2\0\2\11"+ + "\1\103\13\11\7\0\1\11\1\0\1\11\2\0\1\11"+ + "\6\0\2\11\1\0\1\11\1\0\5\11\1\0\1\11"+ + "\2\0\3\11\1\104\7\11\1\105\2\11\7\0\1\11"+ + "\1\0\1\11\2\0\1\11\6\0\2\11\1\0\1\11"+ + "\1\0\5\11\1\0\1\11\2\0\2\11\1\106\13\11"+ + "\7\0\1\11\1\0\1\11\2\0\1\11\6\0\2\11"+ + "\1\0\1\11\1\0\5\11\1\0\1\11\2\0\1\11"+ + "\1\107\14\11\7\0\1\11\1\0\1\11\2\0\1\11"+ + "\11\0\1\110\4\0\2\110\50\0\1\111\1\0\1\111"+ + "\1\0\3\111\1\0\1\111\5\0\3\111\1\0\1\111"+ + "\15\0\1\111\16\0\1\112\1\0\1\112\1\0\3\112"+ + "\1\0\1\112\5\0\3\112\1\0\1\112\15\0\1\112"+ + "\7\0\1\73\54\0\5\74\1\113\51\74\4\0\1\73"+ + "\1\75\62\0\1\114\1\0\1\114\1\0\3\114\1\0"+ + "\1\114\5\0\3\114\1\0\1\114\15\0\1\114\16\0"+ + "\1\115\1\0\1\43\2\0\2\115\1\47\1\50\6\0"+ + "\1\50\37\0\1\101\1\0\1\101\2\0\2\101\45\0"+ + "\2\11\1\0\1\11\1\0\5\11\1\0\1\11\2\0"+ + "\2\11\1\116\13\11\7\0\1\11\1\0\1\11\2\0"+ + "\1\11\6\0\2\11\1\0\1\11\1\0\5\11\1\0"+ + "\1\11\2\0\4\11\1\117\11\11\7\0\1\11\1\0"+ + "\1\11\2\0\1\11\6\0\2\11\1\0\1\11\1\0"+ + "\5\11\1\0\1\11\2\0\1\120\15\11\7\0\1\11"+ + "\1\0\1\11\2\0\1\11\6\0\2\11\1\0\1\11"+ + "\1\0\5\11\1\0\1\11\2\0\10\11\1\121\5\11"+ + "\7\0\1\11\1\0\1\11\2\0\1\11\6\0\2\11"+ + "\1\0\1\11\1\0\5\11\1\0\1\11\2\0\4\11"+ + "\1\122\11\11\7\0\1\11\1\0\1\11\2\0\1\11"+ + "\11\0\1\123\4\0\2\123\50\0\1\124\1\0\1\124"+ + "\1\0\3\124\1\0\1\124\5\0\3\124\1\0\1\124"+ + "\15\0\1\124\16\0\1\125\1\0\1\125\1\0\3\125"+ + "\1\0\1\125\5\0\3\125\1\0\1\125\15\0\1\125"+ + "\5\0\4\74\1\73\1\113\51\74\11\0\1\126\1\0"+ + "\1\126\1\0\3\126\1\0\1\126\5\0\3\126\1\0"+ + "\1\126\15\0\1\126\16\0\1\127\1\0\1\43\2\0"+ + "\2\127\1\47\1\50\6\0\1\50\34\0\2\11\1\0"+ + "\1\11\1\0\5\11\1\0\1\11\2\0\5\11\1\130"+ + "\10\11\7\0\1\11\1\0\1\11\2\0\1\11\6\0"+ + "\2\11\1\0\1\11\1\0\5\11\1\0\1\11\2\0"+ + "\14\11\1\131\1\11\7\0\1\11\1\0\1\11\2\0"+ + "\1\11\6\0\2\11\1\0\1\11\1\0\5\11\1\0"+ + "\1\11\2\0\4\11\1\132\11\11\7\0\1\11\1\0"+ + "\1\11\2\0\1\11\11\0\1\133\1\0\1\133\1\0"+ + "\3\133\1\0\1\133\5\0\3\133\1\0\1\133\15\0"+ + "\1\133\16\0\1\134\1\0\1\43\2\0\2\134\1\47"+ + "\1\50\6\0\1\50\34\0\2\11\1\0\1\11\1\0"+ + "\5\11\1\0\1\11\2\0\6\11\1\135\7\11\7\0"+ + "\1\11\1\0\1\11\2\0\1\11\6\0\2\11\1\0"+ + "\1\11\1\0\5\11\1\0\1\11\2\0\15\11\1\136"+ + "\7\0\1\11\1\0\1\11\2\0\1\11\11\0\1\137"+ + "\1\0\1\137\1\0\3\137\1\0\1\137\5\0\3\137"+ + "\1\0\1\137\15\0\1\137\16\0\1\140\1\0\1\43"+ + "\2\0\2\140\1\47\1\50\6\0\1\50\34\0\2\11"+ + "\1\0\1\11\1\0\5\11\1\0\1\11\2\0\1\141"+ + "\15\11\7\0\1\11\1\0\1\11\2\0\1\11\6\0"+ + "\2\11\1\0\1\11\1\0\5\11\1\0\1\11\2\0"+ + "\1\142\15\11\7\0\1\11\1\0\1\11\2\0\1\11"+ + "\11\0\1\143\1\0\1\143\1\0\3\143\1\0\1\143"+ + "\5\0\3\143\1\0\1\143\15\0\1\143\16\0\1\144"+ + "\1\0\1\43\2\0\2\144\1\47\1\50\6\0\1\50"+ + "\34\0\2\11\1\0\1\11\1\0\5\11\1\0\1\11"+ + "\2\0\4\11\1\145\11\11\7\0\1\11\1\0\1\11"+ + "\2\0\1\11\11\0\1\146\1\0\1\146\1\0\3\146"+ + "\1\0\1\146\5\0\3\146\1\0\1\146\15\0\1\146"+ + "\16\0\1\147\1\0\1\43\2\0\2\147\1\47\1\50"+ + "\6\0\1\50\34\0\2\11\1\0\1\11\1\0\5\11"+ + "\1\0\1\11\2\0\3\11\1\150\12\11\7\0\1\11"+ + "\1\0\1\11\2\0\1\11\11\0\1\151\1\0\1\151"+ + "\1\0\3\151\1\0\1\151\5\0\3\151\1\0\1\151"+ + "\15\0\1\151\16\0\1\152\1\0\1\43\2\0\2\152"+ + "\1\47\1\50\6\0\1\50\37\0\1\153\1\0\1\43"+ + "\2\0\2\153\1\47\1\50\6\0\1\50\37\0\1\154"+ + "\1\0\1\43\2\0\2\154\1\47\1\50\6\0\1\50"+ + "\37\0\1\155\1\0\1\43\2\0\2\155\1\47\1\50"+ + "\6\0\1\50\37\0\1\156\1\0\1\43\2\0\2\156"+ + "\1\47\1\50\6\0\1\50\37\0\1\157\1\0\1\43"+ + "\2\0\2\157\1\47\1\50\6\0\1\50\37\0\1\160"+ + "\1\0\1\43\2\0\2\160\1\47\1\50\6\0\1\50"+ + "\26\0"; + + private static int [] zzUnpackTrans() { + int [] result = new int[3713]; + int offset = 0; + offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackTrans(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + value--; + do result[j++] = value; while (--count > 0); + } + return j; + } + + + /* error codes */ + private static final int ZZ_UNKNOWN_ERROR = 0; + private static final int ZZ_NO_MATCH = 1; + private static final int ZZ_PUSHBACK_2BIG = 2; + + /* error messages for the codes above */ + private static final String ZZ_ERROR_MSG[] = { + "Unkown internal scanner error", + "Error: could not match input", + "Error: pushback value was too large" + }; + + /** + * ZZ_ATTRIBUTE[aState] contains the attributes of state aState + */ + private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); + + private static final String ZZ_ATTRIBUTE_PACKED_0 = + "\3\0\1\11\1\1\1\11\14\1\7\11\2\1\1\11"+ + "\1\1\1\11\1\1\1\0\3\1\1\0\3\1\2\0"+ + "\4\1\1\11\1\1\2\11\1\1\6\11\2\1\1\11"+ + "\2\0\4\1\1\0\5\1\4\0\7\1\1\11\1\0"+ + "\1\11\23\1\1\11\7\1"; + + private static int [] zzUnpackAttribute() { + int [] result = new int[112]; + int offset = 0; + offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAttribute(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + /** the input device */ + private java.io.Reader zzReader; + + /** the current state of the DFA */ + private int zzState; + + /** the current lexical state */ + private int zzLexicalState = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; + + /** the textposition at the last accepting state */ + private int zzMarkedPos; + + /** the current text position in the buffer */ + private int zzCurrentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int zzStartRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int zzEndRead; + + /** number of newlines encountered up to the start of the matched text */ + private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + private int yycolumn; + + /** + * zzAtBOL == true <=> the scanner is currently at the beginning of a line + */ + private boolean zzAtBOL = true; + + /** zzAtEOF == true <=> the scanner is at the EOF */ + private boolean zzAtEOF; + + /** denotes if the user-EOF-code has already been executed */ + private boolean zzEOFDone; + + /** + * The number of occupied positions in zzBuffer beyond zzEndRead. + * When a lead/high surrogate has been read from the input stream + * into the final zzBuffer position, this will have a value of 1; + * otherwise, it will have a value of 0. + */ + private int zzFinalHighSurrogate = 0; + + /* user code: */ + + StringBuilder string = new StringBuilder(); + + private static String xmlTagName = ""; + + public int yychar() { + return yychar; + } + + private final Stack pushedBack = new Stack<>(); + + public int yyline() { + return yyline + 1; + } + + public void pushback(ParsedSymbol symb) { + pushedBack.push(symb); + last = null; + } + + ParsedSymbol last; + public ParsedSymbol lex() throws java.io.IOException, Amf3ParseException{ + ParsedSymbol ret = null; + if (!pushedBack.isEmpty()){ + ret = last = pushedBack.pop(); + } else { + ret = last = yylex(); + } + return ret; + } + + + + /** + * Creates a new scanner + * + * @param in the java.io.Reader to read input from. + */ + public Amf3Lexer(java.io.Reader in) { + this.zzReader = in; + } + + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char [] zzUnpackCMap(String packed) { + char [] map = new char[0x110000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 2856) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do map[j++] = value; while (--count > 0); + } + return map; + } + + + /** + * Refills the input buffer. + * + * @return false, iff there was new input. + * + * @exception java.io.IOException if any I/O-Error occurs + */ + private boolean zzRefill() throws java.io.IOException { + + /* first: make room (if you can) */ + if (zzStartRead > 0) { + zzEndRead += zzFinalHighSurrogate; + zzFinalHighSurrogate = 0; + System.arraycopy(zzBuffer, zzStartRead, + zzBuffer, 0, + zzEndRead-zzStartRead); + + /* translate stored positions */ + zzEndRead-= zzStartRead; + zzCurrentPos-= zzStartRead; + zzMarkedPos-= zzStartRead; + zzStartRead = 0; + } + + /* is the buffer big enough? */ + if (zzCurrentPos >= zzBuffer.length - zzFinalHighSurrogate) { + /* if not: blow it up */ + char newBuffer[] = new char[zzBuffer.length*2]; + System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); + zzBuffer = newBuffer; + zzEndRead += zzFinalHighSurrogate; + zzFinalHighSurrogate = 0; + } + + /* fill the buffer with new input */ + int requested = zzBuffer.length - zzEndRead; + int totalRead = 0; + while (totalRead < requested) { + int numRead = zzReader.read(zzBuffer, zzEndRead + totalRead, requested - totalRead); + if (numRead == -1) { + break; + } + totalRead += numRead; + } + + if (totalRead > 0) { + zzEndRead += totalRead; + if (totalRead == requested) { /* possibly more input available */ + if (Character.isHighSurrogate(zzBuffer[zzEndRead - 1])) { + --zzEndRead; + zzFinalHighSurrogate = 1; + } + } + return false; + } + + // totalRead = 0: End of stream + return true; + } + + + /** + * Closes the input stream. + */ + public final void yyclose() throws java.io.IOException { + zzAtEOF = true; /* indicate end of file */ + zzEndRead = zzStartRead; /* invalidate buffer */ + + if (zzReader != null) + zzReader.close(); + } + + + /** + * Resets the scanner to read from a new input stream. + * Does not close the old reader. + * + * All internal variables are reset, the old input stream + * cannot be reused (internal buffer is discarded and lost). + * Lexical state is set to ZZ_INITIAL. + * + * Internal scan buffer is resized down to its initial length, if it has grown. + * + * @param reader the new input stream + */ + public final void yyreset(java.io.Reader reader) { + zzReader = reader; + zzAtBOL = true; + zzAtEOF = false; + zzEOFDone = false; + zzEndRead = zzStartRead = 0; + zzCurrentPos = zzMarkedPos = 0; + zzFinalHighSurrogate = 0; + yyline = yychar = yycolumn = 0; + zzLexicalState = YYINITIAL; + if (zzBuffer.length > ZZ_BUFFERSIZE) + zzBuffer = new char[ZZ_BUFFERSIZE]; + } + + + /** + * Returns the current lexical state. + */ + public final int yystate() { + return zzLexicalState; + } + + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + public final void yybegin(int newState) { + zzLexicalState = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + public final String yytext() { + return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead ); + } + + + /** + * Returns the character at position pos from the + * matched text. + * + * It is equivalent to yytext().charAt(pos), but faster + * + * @param pos the position of the character to fetch. + * A value from 0 to yylength()-1. + * + * @return the character at position pos + */ + public final char yycharat(int pos) { + return zzBuffer[zzStartRead+pos]; + } + + + /** + * Returns the length of the matched text region. + */ + public final int yylength() { + return zzMarkedPos-zzStartRead; + } + + + /** + * Reports an error that occured while scanning. + * + * In a wellformed scanner (no or only correct usage of + * yypushback(int) and a match-all fallback rule) this method + * will only be called with things that "Can't Possibly Happen". + * If this method is called, something is seriously wrong + * (e.g. a JFlex bug producing a faulty scanner etc.). + * + * Usual syntax/scanner level error handling should be done + * in error fallback rules. + * + * @param errorCode the code of the errormessage to display + */ + private void zzScanError(int errorCode) { + String message; + try { + message = ZZ_ERROR_MSG[errorCode]; + } + catch (ArrayIndexOutOfBoundsException e) { + message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; + } + + throw new Error(message); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + public void yypushback(int number) { + if ( number > yylength() ) + zzScanError(ZZ_PUSHBACK_2BIG); + + zzMarkedPos -= number; + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception java.io.IOException if any I/O-Error occurs + */ + public ParsedSymbol yylex() throws java.io.IOException, Amf3ParseException { + int zzInput; + int zzAction; + + // cached fields: + int zzCurrentPosL; + int zzMarkedPosL; + int zzEndReadL = zzEndRead; + char [] zzBufferL = zzBuffer; + char [] zzCMapL = ZZ_CMAP; + + int [] zzTransL = ZZ_TRANS; + int [] zzRowMapL = ZZ_ROWMAP; + int [] zzAttrL = ZZ_ATTRIBUTE; + + while (true) { + zzMarkedPosL = zzMarkedPos; + + yychar+= zzMarkedPosL-zzStartRead; + + zzAction = -1; + + zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; + + zzState = ZZ_LEXSTATE[zzLexicalState]; + + // set up zzAction for empty match case: + int zzAttributes = zzAttrL[zzState]; + if ( (zzAttributes & 1) == 1 ) { + zzAction = zzState; + } + + + zzForAction: { + while (true) { + + if (zzCurrentPosL < zzEndReadL) { + zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL, zzEndReadL); + zzCurrentPosL += Character.charCount(zzInput); + } + else if (zzAtEOF) { + zzInput = YYEOF; + break zzForAction; + } + else { + // store back cached positions + zzCurrentPos = zzCurrentPosL; + zzMarkedPos = zzMarkedPosL; + boolean eof = zzRefill(); + // get translated positions and possibly new buffer + zzCurrentPosL = zzCurrentPos; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + zzEndReadL = zzEndRead; + if (eof) { + zzInput = YYEOF; + break zzForAction; + } + else { + zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL, zzEndReadL); + zzCurrentPosL += Character.charCount(zzInput); + } + } + int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ]; + if (zzNext == -1) break zzForAction; + zzState = zzNext; + + zzAttributes = zzAttrL[zzState]; + if ( (zzAttributes & 1) == 1 ) { + zzAction = zzState; + zzMarkedPosL = zzCurrentPosL; + if ( (zzAttributes & 8) == 8 ) break zzForAction; + } + + } + } + + // store back cached position + zzMarkedPos = zzMarkedPosL; + + switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { + case 1: + { + } + case 36: break; + case 2: + { yyline++; + } + case 37: break; + case 3: + { /*ignore*/ + } + case 38: break; + case 4: + { return new ParsedSymbol(SymbolGroup.IDENTIFIER, SymbolType.IDENTIFIER, yytext()); + } + case 39: break; + case 5: + { return new ParsedSymbol(SymbolGroup.INTEGER, SymbolType.INTEGER, Long.parseLong((yytext()))); + } + case 40: break; + case 6: + { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.CURLY_OPEN, yytext()); + } + case 41: break; + case 7: + { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.CURLY_CLOSE, yytext()); + } + case 42: break; + case 8: + { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.BRACKET_OPEN, yytext()); + } + case 43: break; + case 9: + { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.BRACKET_CLOSE, yytext()); + } + case 44: break; + case 10: + { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.COMMA, yytext()); + } + case 45: break; + case 11: + { return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.COLON, yytext()); + } + case 46: break; + case 12: + { string.setLength(0); + yybegin(STRING); + } + case 47: break; + case 13: + { string.append(yytext()); + } + case 48: break; + case 14: + { yybegin(YYINITIAL); yyline++; + } + case 49: break; + case 15: + { yybegin(YYINITIAL); + // length also includes the trailing quote + return new ParsedSymbol(SymbolGroup.STRING, SymbolType.STRING, string.toString()); + } + case 50: break; + case 16: + { return new ParsedSymbol(SymbolGroup.IDENTIFIER, SymbolType.REFERENCE, yytext().substring(1)); + } + case 51: break; + case 17: + { return new ParsedSymbol(SymbolGroup.INTEGER, SymbolType.INTEGER, Long.parseLong(yytext(), 8)); + } + case 52: break; + case 18: + { return new ParsedSymbol(SymbolGroup.DOUBLE, SymbolType.DOUBLE, Double.parseDouble((yytext()))); + } + case 53: break; + case 19: + { string.append('\\'); /*illegal escape sequence*/ + } + case 54: break; + case 20: + { string.append('\\'); + } + case 55: break; + case 21: + { string.append('\n'); + } + case 56: break; + case 22: + { string.append('\f'); + } + case 57: break; + case 23: + { string.append('\t'); + } + case 58: break; + case 24: + { string.append('\r'); + } + case 59: break; + case 25: + { string.append('\"'); + } + case 60: break; + case 26: + { string.append('\b'); + } + case 61: break; + case 27: + { string.append('\''); + } + case 62: break; + case 28: + { return new ParsedSymbol(SymbolGroup.INTEGER, SymbolType.INTEGER, Long.parseLong(yytext().substring(2), 16)); + } + case 63: break; + case 29: + { return new ParsedSymbol(SymbolGroup.GLOBALCONST, SymbolType.NULL, yytext()); + } + case 64: break; + case 30: + { return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.TRUE, yytext()); + } + case 65: break; + case 31: + { char val = (char) Integer.parseInt(yytext().substring(1), 8); + string.append(val); + } + case 66: break; + case 32: + { char val = (char) Integer.parseInt(yytext().substring(2), 16); + string.append(val); + } + case 67: break; + case 33: + { return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.FALSE, yytext()); + } + case 68: break; + case 34: + { return new ParsedSymbol(SymbolGroup.UNKNOWN, SymbolType.UNKNOWN, yytext()); + } + case 69: break; + case 35: + { return new ParsedSymbol(SymbolGroup.GLOBALCONST, SymbolType.UNDEFINED, yytext()); + } + case 70: break; + default: + if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { + zzAtEOF = true; + { + return new ParsedSymbol(SymbolGroup.EOF, SymbolType.EOF, null); + } + } + else { + zzScanError(ZZ_NO_MATCH); + } + } + } + } + + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3ParseException.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3ParseException.java new file mode 100644 index 000000000..0f343f283 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/Amf3ParseException.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.amf.amf3; + +import com.jpexs.decompiler.flash.ParseException; + +/** + * + * @author JPEXS + */ +public class Amf3ParseException extends ParseException { + + public Amf3ParseException(String text, long line) { + super(text, line); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/ParsedSymbol.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/ParsedSymbol.java new file mode 100644 index 000000000..c2244cbf6 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/ParsedSymbol.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.amf.amf3; + +/** + * + * @author JPEXS + */ +public class ParsedSymbol { + + public SymbolGroup group; + + public Object value; + + public SymbolType type; + + public ParsedSymbol(SymbolGroup group, SymbolType type) { + this.group = group; + this.type = type; + this.value = null; + } + + public ParsedSymbol(SymbolGroup group, SymbolType type, Object value) { + this.group = group; + this.type = type; + this.value = value; + } + + @Override + public String toString() { + return group.toString() + " " + type.toString() + " " + (value != null ? value.toString() : ""); + } + + public boolean isType(Object... types) { + for (Object t : types) { + if (t instanceof SymbolGroup) { + if (group == t) { + return true; + } + } + if (t instanceof SymbolType) { + if (type == t) { + return true; + } + } + } + return false; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolGroup.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolGroup.java new file mode 100644 index 000000000..a6a8b953f --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolGroup.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.amf.amf3; + +/** + * + * @author JPEXS + */ +public enum SymbolGroup { + OPERATOR, + KEYWORD, + STRING, + COMMENT, + IDENTIFIER, + INTEGER, + DOUBLE, + EOF, + GLOBALCONST, + UNKNOWN +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolType.java new file mode 100644 index 000000000..03fad7666 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/amf/amf3/SymbolType.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.importers.amf.amf3; + +/** + * + * @author JPEXS + */ +public enum SymbolType { + NULL, + UNDEFINED, + FALSE, + TRUE, + CURLY_OPEN, + CURLY_CLOSE, + BRACKET_OPEN, + BRACKET_CLOSE, + COMMA, + COLON, + UNKNOWN, + REFERENCE, + IDENTIFIER, + INTEGER, + STRING, + DOUBLE, + EOF +} diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java index af5abf607..49ecef29f 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/amf/amf3/Amf3OutputStreamTest.java @@ -63,18 +63,6 @@ public class Amf3OutputStreamTest { }; } - private static byte[] readFile(File file) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte buf[] = new byte[1024]; - int cnt; - try (FileInputStream fis = new FileInputStream(file)) { - while ((cnt = fis.read(buf)) > 0) { - baos.write(buf, 0, cnt); - } - } - return baos.toByteArray(); - } - @Test(dataProvider = "files") public void testRecompile(String fileName) throws FileNotFoundException, IOException, NoSerializerExistsException { diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/importers/amf/amf3/parser/Amf3ImporterTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/importers/amf/amf3/parser/Amf3ImporterTest.java new file mode 100644 index 000000000..5ac5fae8b --- /dev/null +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/importers/amf/amf3/parser/Amf3ImporterTest.java @@ -0,0 +1,85 @@ +package com.jpexs.decompiler.flash.importers.amf.amf3.parser; + +import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3ParseException; +import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3Importer; +import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; +import com.jpexs.decompiler.flash.amf.amf3.Amf3InputStream; +import com.jpexs.decompiler.flash.amf.amf3.NoSerializerExistsException; +import com.jpexs.decompiler.flash.amf.amf3.ObjectTypeSerializeHandler; +import com.jpexs.decompiler.flash.amf.amf3.Pair; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.MemoryInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class Amf3ImporterTest { + + @DataProvider(name = "files") + public static Object[][] provideSamples() { + return new Object[][]{ + {"all.bin"} + }; + } + + private Map getSerializers() { + Map serializers = new HashMap<>(); + serializers.put("CustomClass", new ObjectTypeSerializeHandler() { + @Override + public List> readObject(String className, InputStream is) throws IOException { + List> members = new ArrayList<>(); + members.add(new Pair<>("val8", (long) is.read())); + members.add(new Pair<>("val32", (long) ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read())))); + return members; + } + + @Override + public void writeObject(List> members, OutputStream os) throws IOException { + Map memberMap = new HashMap<>(); + for (Pair m : members) { + memberMap.put(m.getFirst(), m.getSecond()); + } + os.write((int) (long) (Long) memberMap.get("val8")); + long val32 = (long) (Long) memberMap.get("val32"); + os.write((int) ((val32 >> 24) & 0xff)); + os.write((int) ((val32 >> 16) & 0xff)); + os.write((int) ((val32 >> 8) & 0xff)); + os.write((int) (val32 & 0xff)); + } + }); + return serializers; + } + + @Test(dataProvider = "files") + public void testRecompile(String fileName) throws IOException, NoSerializerExistsException, Amf3ParseException { + + String originalFile = "testdata/amf3/generated/" + fileName; + + Amf3InputStream is = new Amf3InputStream(new MemoryInputStream(Helper.readFile(originalFile))); + + Object val = is.readValue("testValue", getSerializers()); + String exported1File = "testdata/amf3/generated/exported1." + fileName; + String exported2File = "testdata/amf3/generated/exported2." + fileName; + + String exported1 = Amf3Exporter.amfToString(val); + Helper.writeFile(exported1File, exported1.getBytes("UTF-8")); + Amf3Importer imp = new Amf3Importer(); + Object valImported = imp.stringToAmf(exported1); + String exported2 = Amf3Exporter.amfToString(valImported); + + Helper.writeFile(exported2File, exported2.getBytes("UTF-8")); + + Assert.assertEquals(exported2, exported1); + new File(exported1File).delete(); + new File(exported2File).delete(); + + } +} diff --git a/libsrc/ffdec_lib/testdata/amf3/AmfTest.as b/libsrc/ffdec_lib/testdata/amf3/AmfTest.as index 37c7934fc..83658a2c7 100644 --- a/libsrc/ffdec_lib/testdata/amf3/AmfTest.as +++ b/libsrc/ffdec_lib/testdata/amf3/AmfTest.as @@ -164,6 +164,9 @@ package var objWithCustom:ObjectWithCustom = new ObjectWithCustom("1","2",custom,"4","5","6"); writeToFile("noserializer_object_sealed.bin", objWithCustom); + + + writeToFile("simple.bin", {"a":7, "b":29}); } } } \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/amf3/AmfTest.swf b/libsrc/ffdec_lib/testdata/amf3/AmfTest.swf index eed907c1861886a822e576ff7dfffc76e21f47fb..b7547e8c043e2ec9b6d85a77a402dcce92cbb41b 100644 GIT binary patch delta 2033 zcmVYu}mjr@1byoEqWhwkRtfMZJXn zYAe#g*mu?1q6KA!6dqR)fqo)L|G;#3D1z$fN%bT3=csH;SQ|NHIXbqw-t#jVZm=i= zy$4(N>_^3c)QA@9ZJ4h+%>bg%k5L{NBE(dBeDg0kQlHG4V8=egZL@=O~vYICV1dSAEO|9Dvnu)YH(#j3TF8v}w+ziI(AfbD@_= zOI0QVX2Zu?)I6oVt?+h~^H^3E-v+;%S>o=OzNvvqbN9r z0F?0XKx_AUPpSxjiJq>-NX9rJvwziOCp%0PwWwpLSd#-Ev=Z7pRg*RTq*BBnHD0wa zpCqU>VGjn}4_^;s_0Q32wM?P%6Wu6ADt+K_Jb-j!^>Q@k41qD032cQRrFQ!Cg$7^v zIwU_?!RiUq@O6>)`AH`V2F6-=`Ai|}XcCF4n@d9PHN3}J4X3q$NLEIs(|<<5pGy24 z@m>v$@g<`KE%>-|in8_r>{%zro;QNeC4G1%aESa(l-FZtdX=p7r*TaSu(?gM<79?wjf z=v(A?SxHR5v^69PboR$XMhdO9(5|50C~m}#hab=|dns4BkCgO+`$7>z#8a1sJSKSc zaMspLeKz{gY@Ccyn?lf6v&J0rMna+Q;O|zi8B$9F5=-6ne*(A2e1G?mRrl=mS!d@{ z*_w>Z%CNF2$_UXS?jYCqFRZNAErqK=#5zZ_XTVnYu*<$;So&ZrI#0dHMlmyUGfuty zOZfRFY%JLAH&<>(amyxfTtdUo#+>a2ch~<_rSN8{WKzd&h0-tIg#76>l8YpUm@fEm zZTRV4Ew-Fz73<39GJk>s2bo@3QK)d!!P46P)sPrcdaaN>Fz+uxh-g@R^nHi|U@s`> zx!M!#2v%>O`)3&Z`Q0r4i{OF+ER$`Fl{m({#jKUgFC6Az1L5sj-f!td-sNG$F;}}B zz8L(GPXpPBQ4pH6=Bveo7KQ|VBDkpBL;kY!QguQ(^Z!^n6MtSxBvyD82f%F$xap#J zbEA3|0SPj)bYd#yB;(qFCR9=}U8zM3apUYi5&FJR^vF+Bzk;d9BvWqakZ)8U)ym0E zu8bzY|0Ku8!0{^3(zwVwbE56 zZ?a%6n&ROw0e{L)JHMbeWnI4@5B?Y$p4C*v*t_3u$)E+==e8bj+PB_Y`F)|4CEIDU@czQb&o&7yG2AG?dZ-x zfh?TzMea3uvH7V)4s_wj>+I+Emul?VC_giP+Fb7gxjWRMWo3zS75kfTB7_ihcn8P^ zj>`{VFndJ#qVBYvW8YSkJn(d`L&S08%X!fQj zL-w%#l}n7r;Hf{Vze=spcK#0XVo91q)y>wqT7NWlf|cb{mfsSt+f)s7THH7M@-z`X=!*Ot;1cuHJL|P;&JBHy%am$aqnyI2#}5rksM*j0pkSdIJ(@0eU=A z6X7beuIhzsWFF1qB0Au7_g@ zcVn*2^9N`cNtVugAK-SjamTVp!HJjNb!1();_bg(5c|6EtuNQv%q1F!ebnQZG-LD$ z6_&ALU}1@|>-c1T|M}#a#iNj-e_jEntU0DspixN;mq@tizq92efuL?fffXa~IXdb{ zGwmMdv-GU>6M39cZfdSWTml#}OR>qs^Bhy=k46Lmquz0^2=DPOlJJ<^8 z6bmevwHJ?vowHMJJkVNIyH^$+ss7D}1yC^(U|@0XqC>9YPhfgpHXPKwCGx^8;YezK zG3wL;ajXJz=6@k}F;@b}N9s>lrdWM=RDK8J)H;3wIZCUjk+K<#DAIC4ZphLg;(rZj zW5nXdyoif+B+ldMAi!~gn1O4YJfE^&6*Zg+3|gD#LEAgu)PeK_LCPfu18lMa=;}gI z&FY7$8j&KuG4nyomaz*re=BK_YdzOO6KLu0L6SKLMX-~P1Cci=w0Eh{oVu7pI(7zC zW7cfj91G9a+CbdS*>7~ZDIU~bN`FUOm{FXYa7XcSL$1QuQ8qeJ#NJ86gB0m!Y+DR#EGbvEfVk{c%-?04{1#PRdul@F)kEd) z^jE%JR^*c0x;`&*Q8Ebc)rlDjkWTd4++-@;p+hE?WQra#d=JENPHc#f-uRNLL5x&U zHQOG44L({;&z%ucW^;WdpMM}HY9t^L;!s~55fz{t1?jLuKTk%LKW+uFp$}fLOQqJ& zg7P>{N$d&QF4I8`R9u!ihqz+q$$NGSUbGw z8nU8(?FkwBHp|xCNPK+7=Liw@$6Ay1{bXA*!0sN!xm883$G@|`;yQDRuQ)4zrQ_GI zNZX=)XYa6Ph|PIhH&wcBhXbu@GV%2elOhfInXc?Ijw=kPH&AqTzc8o}J|2{JpVr*O zAB7%}HPi-Ff)Em%eSe2#HvS7W%z;Gy)IFytI)Z9>ra{j^GYHW&;Soo++L`HyH^6>d zKQ@9!S2P8bt-o1DZUE_RWhOZZiRoqLNK>761rf4G(Z&qj)3arOEMg;RlgxvLRK^%V zW1C)z5f^frR1PfalE?5VulAL&J6iD#mUQ2J9RJgMnC%rtBR_J!;WA0Gb6n)>RU!m(Y_*OyTQ$&i%>qRy z6mR4mhQP_CyMn!a5Ni@!L=kCB841l!)I*N~=6vgyYh`3t2xDY60*DWiFMest%xw`^ zs9$wTJH~!}tbe?g+iAcI*rrcKB9g2E^Ih}2E^sEh#_m-HC3J8eTwrv0kXAax=Ds5A zc*~H?_;)#biJL-r-~c3V6{u{J>c=mbh>OV;VM&K0dFSJFn?FjP;Mnf4u}X(Fm)^GB9n@3 znq{2;yjN_8R%u?oG0=w{8cQ|krJX9s^QOj@)%Zjg>ok3a0yvHDp&+;1~~!CvA9ao29~SA4SWrEIBtrky0C z@y}xsIZO~}PG^yKGL@i+XVms4>cXMV)cX@gASMbMq~i6`^YSH zOMjs(lf1PXgbw5EC*v?5?XznjlCZGK`JVyZ{}4Da(3rmtJBv6*G(|QZxj6FHmINR* zP_6Z!;R_+dTB}^(@A?Ju6G3?vHAgDk@(^(E<>DvO}~)`Hor|iAPKUs z1&CzKxP$N&Pf|+8EP;ueEIyFVv5j`?HF^T!%T?USRgnck0JLPBPhPOO_3Fd2IDb4U z&;elzd9|-pj!p`cIifR*Bp(7+*@&|04Rg$1Q1eTWUK^-*HDnv2DWJ z5yV0l^bU>WB{-Gg*b!f2su>Hz_*^3ecKX$1eChI6vkUm$LIzrTbvF%`|C{p&;v%KV ANdN!< diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/all.bin b/libsrc/ffdec_lib/testdata/amf3/generated/all.bin index 22b5dc4c008098fca024f38cc36163af09c22079..e9f33e746c12fe6e02b8c0fd65d92f680074aad8 100644 GIT binary patch literal 765 zcmYjPUvJYe5KkH>U6XZLX#rIgYo~&?mua@szfx6oV-Fx9fy8+B+{B$CvP4RgZQZBq z2Vh@#LJQUk8v?rZU_q+4&?(DN2y^Dt=Weal8{47boeT=h%ipIs}+TeP`^|XzK zD&Z>0=Mi%&+WM_0W!5X~div=Hm*?44;kVFWm$TGFedY5K)rz*+Y<`Op-2N0Lj`3ip z50PWlV4o~#v}C0w9FRN?*dmNsptU?KQy=f5Ao0s+vPhEYWKPp5rIT}EC+7lcq8^#A zGIo)sbhTf;xP0%z9?8<28OOLUDYjwzrHB5pIz&?1osMMvz@jKAi2p;O@>M15$-7ik zs+4uhpq4SW4F5Im$v{9ewx?g_H)qekI$GC*1seKUn8Y+))lAKDg70jFEcco%v(GUr zd|dd~4@nhygiq}3w{S83GE2kw)~bG$whld_5O{*elE3)VB7Rubcw3K_v9gS(HpC=m zw=@gy*;yE8fd9gu7D9wB6Q8IlD!d&GEMX8Zb)bV1Fi!Ajhgxj|s3E9s9aJGAg3+vF zB>ELUwCBPhiSsDZTNsjHJM{|}VItq=@Esg)TMFcsnd?^zDRnp^3Ysa%@jP#1gGGLpA&%FXr%Jgb2Rk7!0+Vc literal 765 zcmYjP-EPw`6m}ZNrOCQdb^Me-+o_=S2F-T*D@9c|b^#I+NQ`UCZQLm$OQbX%D_1-L zx9~W90C))=f*UT1<5+Y@a*mGueU8s}KHD*>n6NO;pmj`#EEP+3BNkbbzKOGH1?+aa zKLCIunSaaNJEjALnQt<2mZp4jP(C|?+WX_Sfs1vM-b`AGgmac{AQmVkK zEDhr;RQ;gx3p`=5xZ+uOBVGo{dL9XD`*5kEK?%FPj)G)SCjK%>rv99#Q_lUOZq;M zI^Qn?!|od9#=M0`(6%kugDv^>A#LIzTgQP|hOr1hn~XKDXeuY7F&ZNCi|Z9#>pl+J zWTc@Ns@YlvY@d57X8j^>r*D7C_%*vI>IWWC!pSQfrgX479VNwU5k*O%#{aEQ*M&ST z8S3AClrA}}NMisycZpmrxq5XxU`rmYM5*iHV*Y#S4x414>AA;BZA|yWk^({LwZBc$ uXM2yq6FO1X6xP&wJZ@l*>A`nS==cso%kZR1ynNWs(=Hv|IabeZ8UFx?Amuaw diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/custom.bin b/libsrc/ffdec_lib/testdata/amf3/generated/custom.bin index c3bed6b07..74ae73be5 100644 --- a/libsrc/ffdec_lib/testdata/amf3/generated/custom.bin +++ b/libsrc/ffdec_lib/testdata/amf3/generated/custom.bin @@ -1,3 +1,3 @@ - member3member1member2 -CustomClassÿÿö< \ No newline at end of file + member3member2 +CustomClassÿÿö<member1 \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_dictionary_key.bin b/libsrc/ffdec_lib/testdata/amf3/generated/noserializer_dictionary_key.bin index 24d9b65ac5dc1a4d7c6463662f0261260e75e4aa..28445e990a36d0e51c174ee88df0b8fb0e654f5e 100644 GIT binary patch delta 45 zcmZ>9nINOV#+<^&9gR=2SLLxBSu~05Dw+ ANB{r; delta 45 zcmZ>9nINOV#V+n#T3nK!>ztEVTwMSE|2G>p?vRY4)KoU+6gECK=2SLLxBSu~09T6+ ANB{r; diff --git a/libsrc/ffdec_lib/testdata/amf3/generated/simple.bin b/libsrc/ffdec_lib/testdata/amf3/generated/simple.bin new file mode 100644 index 000000000..ab4fd477c --- /dev/null +++ b/libsrc/ffdec_lib/testdata/amf3/generated/simple.bin @@ -0,0 +1,2 @@ + + ba \ No newline at end of file