Fixed #1490, #1493 AS1/2 direct editation of cast op, cast op decompilation

This commit is contained in:
Jindra Petřík
2021-02-13 21:36:08 +01:00
parent 0e0b05947e
commit a954d2f902
14 changed files with 301 additions and 31 deletions

View File

@@ -13,6 +13,8 @@ All notable changes to this project will be documented in this file.
- #1467 AS1/2 direct editation - allow new Number call
- #1489 AS1/2 direct editation - reversed negations
- #1489 AS1/2 direct editation - for in loop
- #1490, #1493 AS1/2 direct editation - cast op
- AS1/2 cast op decompilation
## [13.0.3] - 2021-02-12
### Added

View File

@@ -12,7 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
*/
package com.jpexs.decompiler.flash.action.model;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
@@ -51,10 +52,11 @@ public class CastOpActionItem extends ActionItem {
@Override
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException {
stripQuotes(constructor, localData, writer);
writer.append("(");
object.toString(writer, localData);
writer.append(")");
writer.append(")");
return writer;
}
@Override

View File

@@ -16,7 +16,9 @@
*/
package com.jpexs.decompiler.flash.action.parser.script;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SourceGeneratorLocalData;
import com.jpexs.decompiler.flash.abc.avm2.model.NewObjectAVM2Item;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.model.AsciiToCharActionItem;
import com.jpexs.decompiler.flash.action.model.CallActionItem;
@@ -25,6 +27,7 @@ import com.jpexs.decompiler.flash.action.model.CallMethodActionItem;
import com.jpexs.decompiler.flash.action.model.CastOpActionItem;
import com.jpexs.decompiler.flash.action.model.CharToAsciiActionItem;
import com.jpexs.decompiler.flash.action.model.CloneSpriteActionItem;
import com.jpexs.decompiler.flash.action.model.ConstantPool;
import com.jpexs.decompiler.flash.action.model.DefineLocalActionItem;
import com.jpexs.decompiler.flash.action.model.DeleteActionItem;
import com.jpexs.decompiler.flash.action.model.DirectValueActionItem;
@@ -128,8 +131,13 @@ import com.jpexs.decompiler.flash.action.swf4.ConstantIndex;
import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool;
import com.jpexs.decompiler.flash.ecma.Null;
import com.jpexs.decompiler.flash.ecma.Undefined;
import com.jpexs.decompiler.flash.helpers.CodeFormatting;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.helpers.NulWriter;
import com.jpexs.decompiler.flash.helpers.StringBuilderTextWriter;
import com.jpexs.decompiler.flash.helpers.collections.MyEntry;
import com.jpexs.decompiler.flash.tags.DoInitActionTag;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.graph.CompilationException;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
@@ -159,6 +167,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
@@ -166,16 +175,84 @@ import java.util.List;
*/
public class ActionScript2Parser {
private final int swfVersion;
public static final List<String> BUILTIN_CASTS = Arrays.asList(new String[]{
"flash.display.BitmapData",
"flash.external.ExternalInterface",
"flash.filters.BevelFilter",
"flash.filters.BitmapFilter",
"flash.filters.BlurFilter",
"flash.filters.ColorMatrixFilter",
"flash.filters.ConvolutionFilter",
"flash.filters.DisplacementMapFilter",
"flash.filters.DropShadowFilter",
"flash.filters.GlowFilter",
"flash.filters.GradientBevelFilter",
"flash.filters.GradientGlowFilter",
"flash.geom.ColorTransform",
"flash.geom.Matrix",
"flash.geom.Point",
"flash.geom.Rectangle",
"flash.geom.Transform",
"flash.net.FileReference",
"flash.net.FileReferenceList",
"flash.text.TextRenderer",
"Button",
"Camera",
"Color",
"ContextMenu",
"ContextMenuItem",
"CustomActions",
"Error",
"System.IME",
"LoadVars",
"LocalConnection",
"Microphone",
"MovieClip",
"MovieClipLoader",
"NetConnection",
"NetStream",
"PrintJob",
"System.security",
"SharedObject",
"Sound",
"TextField",
"TextFormat",
"TextSnapshot",
"XML",
"XMLNode",
"XMLSocket",
"XMLUI"
});
public ActionScript2Parser(int swfVersion) {
this.swfVersion = swfVersion;
private final int swfVersion;
private List<String> swfClasses = new ArrayList<>();
public ActionScript2Parser(SWF swf) {
this.swfVersion = swf.version;
parseSwfClasses(swf);
}
private long uniqLast = 0;
private final boolean debugMode = false;
private void parseSwfClasses(SWF swf) {
Map<String, ASMSource> asms = swf.getASMs(false);
for (ASMSource s : asms.values()) {
if (s instanceof DoInitActionTag) {
String exportName = swf.getExportName(((DoInitActionTag) s).spriteId);
if (exportName != null) {
final String PREFIX = "__Packages.";
if (exportName.startsWith(PREFIX)) {
String className = exportName.substring(PREFIX.length());
swfClasses.add(className);
}
}
}
}
}
private String uniqId() {
uniqLast++;
return "" + uniqLast;
@@ -1504,15 +1581,6 @@ public class ActionScript2Parser {
}
lhs = new PostDecrementActionItem(null, null, lhs);
break;
default:
if (lhs instanceof ParenthesisItem) {
if (isType(((ParenthesisItem) lhs).value)) {
GraphTargetItem expr2 = expression(inFunction, inMethod, true, variables, functions);
if (expr2 != null) {
lhs = new CastOpActionItem(null, null, ((ParenthesisItem) lhs).value, expr2);
}
}
}
}
if (debugMode) {
System.out.println("/expression1");
@@ -1764,7 +1832,17 @@ public class ActionScript2Parser {
break;
case NEW:
GraphTargetItem newvar = expressionPrimary(false, inFunction, inMethod, false, variables, functions);//variable(inFunction, inMethod, variables, functions);
if (newvar instanceof ToNumberActionItem) {
if (newvar instanceof CastOpActionItem) {
List<GraphTargetItem> args = new ArrayList<>();
args.add((((CastOpActionItem) newvar).object));
newvar = ((CastOpActionItem) newvar).constructor;
if (newvar instanceof GetMemberActionItem) {
ret = new NewMethodActionItem(null, null, ((GetMemberActionItem) newvar).object, ((GetMemberActionItem) newvar).memberName, args);
} else if (newvar instanceof VariableActionItem) {
ret = new NewObjectActionItem(null, null, newvar, args);
}
}
else if (newvar instanceof ToNumberActionItem) {
List<GraphTargetItem> args = new ArrayList<>();
if (((ToNumberActionItem) newvar).value != null) {
args.add(((ToNumberActionItem) newvar).value);
@@ -1830,12 +1908,42 @@ public class ActionScript2Parser {
return ret;
}
private boolean isCastOp(GraphTargetItem item) {
LocalData localData = LocalData.create(new ConstantPool(constantPool));
List<String> items = new ArrayList<>();
while (item instanceof GetMemberActionItem) {
GetMemberActionItem mem = (GetMemberActionItem) item;
if (mem.memberName instanceof DirectValueActionItem) {
items.add(0, mem.memberName.toStringNoQuotes(localData));
}
item = mem.object;
}
if (item instanceof VariableActionItem) {
VariableActionItem v = (VariableActionItem) item;
items.add(0, v.getVariableName());
}
if (items.isEmpty()) {
return false;
}
String fullName = String.join(".", items);
if (BUILTIN_CASTS.contains(fullName)) {
return true;
}
if (swfClasses.contains(fullName)) {
return true;
}
return false;
}
private GraphTargetItem memberOrCall(GraphTargetItem ret, boolean inFunction, boolean inMethod, List<VariableActionItem> variables, List<FunctionActionItem> functions) throws IOException, ActionParseException {
ParsedSymbol op = lex();
while (op.isType(SymbolType.PARENT_OPEN, SymbolType.BRACKET_OPEN, SymbolType.DOT)) {
if (op.type == SymbolType.PARENT_OPEN) {
List<GraphTargetItem> args = call(inFunction, inMethod, variables, functions);
if (ret instanceof GetMemberActionItem) {
if (isCastOp(ret) && args.size() == 1) {
ret = new CastOpActionItem(null, null, ret, args.get(0));
} else if (ret instanceof GetMemberActionItem) {
GetMemberActionItem mem = (GetMemberActionItem) ret;
ret = new CallMethodActionItem(null, null, mem.object, mem.memberName, args);
} else if (ret instanceof VariableActionItem) {

View File

@@ -12,7 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
*/
package com.jpexs.decompiler.flash.importers;
import com.jpexs.decompiler.flash.action.ConstantPoolTooBigException;
@@ -72,7 +73,7 @@ public class AS2ScriptImporter {
if (new File(fileName).exists()) {
String txt = Helper.readTextFile(fileName);
ActionScript2Parser par = new ActionScript2Parser(asm.getSwf());
try {
asm.setActions(par.actionsFromString(txt));
} catch (ActionParseException ex) {

View File

@@ -46,6 +46,8 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
@@ -394,8 +396,14 @@ public abstract class GraphTargetItem implements Serializable, Cloneable {
}
public String toStringNoQuotes(LocalData localData) {
// todo: honfika: this method should not be called, maybe we should throw an exception
return toString();
try {
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), false);
toStringNoQuotes(writer, localData);
return writer.toString();
} catch (InterruptedException ex) {
//ignore
}
return "";
}
public GraphTextWriter toStringNoQuotes(GraphTextWriter writer, LocalData localData) throws InterruptedException {

View File

@@ -12,7 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
*/
package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.flash.action.Action;
@@ -56,7 +57,7 @@ public class ActionScript2CompilerTest extends ActionScript2TestBase {
SWF swf = new SWF();
ASMSource asm = new DoActionTag(swf);
ActionScript2Parser par = new ActionScript2Parser(swf);
try {
asm.setActions(par.actionsFromString(sourceAsToCompile));
} catch (ActionParseException | CompilationException ex) {

View File

@@ -12,7 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
*/
package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.flash.action.Action;
@@ -52,7 +53,9 @@ public class ActionScript2DeobfuscatorTest extends ActionScript2TestBase {
}
private String recompile(String str) throws ActionParseException, IOException, CompilationException, InterruptedException, TimeoutException {
private String recompile(String str) throws ActionParseException, IOException, CompilationException, InterruptedException, TimeoutException {
SWF swf = new SWF();
swf.version = SWF.DEFAULT_VERSION;
ActionScript2Parser par = new ActionScript2Parser(swf);
HighlightedTextWriter writer = new HighlightedTextWriter(new CodeFormatting(), false);
List<Action> actions = par.actionsFromString(str);
byte[] hex = Action.actionsToBytes(actions, true, SWF.DEFAULT_VERSION);

View File

@@ -12,7 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
*/
package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.flash.action.parser.script.ActionScript2Parser;
@@ -38,7 +39,9 @@ public class ActionScript2ParserTest extends ActionScript2TestBase {
private void parseAS2(String script) {
try {
try {
SWF swf = new SWF();
swf.version = SWF.DEFAULT_VERSION;
ActionScript2Parser par = new ActionScript2Parser(swf);
par.actionsFromString(script);
} catch (IOException | CompilationException | ParseException ex) {
fail("Unable to parse: " + script, ex);

View File

@@ -2024,4 +2024,144 @@ public class ActionScript2Test extends ActionScript2TestBase {
+ "delete \"bagr aa\";\r\n"
);
}
@Test
public void frame74_setPropertyTest() {
compareSrc(74, "trace(\"setPropertyTest\");\r\n"
+ "setProperty(\"_root\", _rotation, 45);\r\n"
+ "_root._rotation = 60;\r\n"
+ "trace(getProperty(\"_root\", _rotation));\r\n"
+ "set(\"_root._rotation\",60);\r\n"
+ "trace(undefined);\r\n"
);
}
@Test
public void frame75_castOpTest() {
compareSrc(75, "trace(\"castOpTest\");\r\n"
+ "var a = 5;\r\n"
+ "var b = null;\r\n"
+ "b = flash.display.BitmapData(a);\r\n"
+ "b = flash.external.ExternalInterface(a);\r\n"
+ "b = flash.filters.BevelFilter(a);\r\n"
+ "b = flash.filters.BitmapFilter(a);\r\n"
+ "b = flash.filters.BlurFilter(a);\r\n"
+ "b = flash.filters.ColorMatrixFilter(a);\r\n"
+ "b = flash.filters.ConvolutionFilter(a);\r\n"
+ "b = flash.filters.DisplacementMapFilter(a);\r\n"
+ "b = flash.filters.DropShadowFilter(a);\r\n"
+ "b = flash.filters.GlowFilter(a);\r\n"
+ "b = flash.filters.GradientBevelFilter(a);\r\n"
+ "b = flash.filters.GradientGlowFilter(a);\r\n"
+ "b = flash.geom.ColorTransform(a);\r\n"
+ "b = flash.geom.Matrix(a);\r\n"
+ "b = flash.geom.Point(a);\r\n"
+ "b = flash.geom.Rectangle(a);\r\n"
+ "b = flash.geom.Transform(a);\r\n"
+ "b = flash.net.FileReference(a);\r\n"
+ "b = flash.net.FileReferenceList(a);\r\n"
+ "b = flash.text.TextRenderer(a);\r\n"
+ "b = Accordion(a);\r\n"
+ "b = Alert(a);\r\n"
+ "b = Array(a);\r\n"
+ "b = BevelFilter(a);\r\n"
+ "b = Binding(a);\r\n"
+ "b = BitmapData(a);\r\n"
+ "b = BitmapFilter(a);\r\n"
+ "b = BlurFilter(a);\r\n"
+ "b = Boolean(a);\r\n"
+ "b = Button(a);\r\n"
+ "b = Camera(a);\r\n"
+ "b = Color(a);\r\n"
+ "b = ColorMatrixFilter(a);\r\n"
+ "b = ColorTransform(a);\r\n"
+ "b = ComboBox(a);\r\n"
+ "b = ComponentMixins(a);\r\n"
+ "b = ContextMenu(a);\r\n"
+ "b = ContextMenuItem(a);\r\n"
+ "b = ConvolutionFilter(a);\r\n"
+ "b = CustomActions(a);\r\n"
+ "b = CheckBox(a);\r\n"
+ "b = DataGrid(a);\r\n"
+ "b = DataHolder(a);\r\n"
+ "b = DataSet(a);\r\n"
+ "b = DataType(a);\r\n"
+ "b = Date(a);\r\n"
+ "b = DateChooser(a);\r\n"
+ "b = DateField(a);\r\n"
+ "b = Delta(a);\r\n"
+ "b = DeltaItem(a);\r\n"
+ "b = DeltaPacket(a);\r\n"
+ "b = DisplacementMapFilter(a);\r\n"
+ "b = DropShadowFilter(a);\r\n"
+ "b = EndPoint(a);\r\n"
+ "b = Error(a);\r\n"
+ "b = ExternalInterface(a);\r\n"
+ "b = FileReference(a);\r\n"
+ "b = FileReferenceList(a);\r\n"
+ "b = FLVPlayback(a);\r\n"
+ "b = Form(a);\r\n"
+ "b = Function(a);\r\n"
+ "b = GlowFilter(a);\r\n"
+ "b = GradientBevelFilter(a);\r\n"
+ "b = GradientGlowFilter(a);\r\n"
+ "b = System.IME(a);\r\n"
+ "b = Label(a);\r\n"
+ "b = List(a);\r\n"
+ "b = Loader(a);\r\n"
+ "b = LoadVars(a);\r\n"
+ "b = LocalConnection(a);\r\n"
+ "b = Log(a);\r\n"
+ "b = Matrix(a);\r\n"
+ "b = MediaController(a);\r\n"
+ "b = MediaDisplay(a);\r\n"
+ "b = MediaPlayback(a);\r\n"
+ "b = Menu(a);\r\n"
+ "b = MenuBar(a);\r\n"
+ "b = Microphone(a);\r\n"
+ "b = MovieClip(a);\r\n"
+ "b = MovieClipLoader(a);\r\n"
+ "b = NetConnection(a);\r\n"
+ "b = NetStream(a);\r\n"
+ "b = Number(a);\r\n"
+ "b = NumericStepper(a);\r\n"
+ "b = Object(a);\r\n"
+ "b = PendingCall(a);\r\n"
+ "b = Point(a);\r\n"
+ "b = PopUpManager(a);\r\n"
+ "b = PrintJob(a);\r\n"
+ "b = ProgressBar(a);\r\n"
+ "b = RadioButton(a);\r\n"
+ "b = RadioButtonGroup(a);\r\n"
+ "b = RDBMSResolver(a);\r\n"
+ "b = Rectangle(a);\r\n"
+ "b = ScrollPane(a);\r\n"
+ "b = System.security(a);\r\n"
+ "b = SharedObject(a);\r\n"
+ "b = Slide(a);\r\n"
+ "b = SOAPCall(a);\r\n"
+ "b = Sound(a);\r\n"
+ "b = String(a);\r\n"
+ "b = TextArea(a);\r\n"
+ "b = TextField(a);\r\n"
+ "b = TextFormat(a);\r\n"
+ "b = TextInput(a);\r\n"
+ "b = TextRenderer(a);\r\n"
+ "b = TextSnapshot(a);\r\n"
+ "b = Transform(a);\r\n"
+ "b = Tree(a);\r\n"
+ "b = TypedValue(a);\r\n"
+ "b = UIScrollBar(a);\r\n"
+ "b = Void(a);\r\n"
+ "b = WebService(a);\r\n"
+ "b = WebServiceConnector(a);\r\n"
+ "b = Window(a);\r\n"
+ "b = XML(a);\r\n"
+ "b = XMLConnector(a);\r\n"
+ "b = XMLNode(a);\r\n"
+ "b = XMLSocket(a);\r\n"
+ "b = XMLUI(a);\r\n"
+ "b = XUpdateResolver(a);\r\n"
);
}
}

View File

@@ -12,7 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
*/
package com.jpexs.decompiler.flash;
import com.jpexs.decompiler.flash.abc.ABC;
@@ -109,7 +110,8 @@ public class DirectEditingTest extends FileTestBase {
asm.getActionScriptSource(writer, null);
String as = writer.toString();
as = asm.removePrefixAndSuffix(as);
as = asm.removePrefixAndSuffix(as);
ActionScript2Parser par = new ActionScript2Parser(swf);
try {
asm.setActions(par.actionsFromString(as));
} catch (ActionParseException | CompilationException ex) {

Binary file not shown.

Binary file not shown.

View File

@@ -3561,7 +3561,7 @@ public class CommandLineArgumentParser {
private static void replaceAS2(String as, ASMSource src) throws IOException, InterruptedException {
System.out.println("Replace AS1/2");
System.out.println("Warning: This feature is EXPERIMENTAL");
ActionScript2Parser par = new ActionScript2Parser(src.getSwf().version);
ActionScript2Parser par = new ActionScript2Parser(src.getSwf());
try {
src.setActions(par.actionsFromString(as));
} catch (ActionParseException ex) {

View File

@@ -1071,7 +1071,7 @@ public class ActionPanel extends JPanel implements SearchListener<ActionSearchRe
private void saveDecompiledButtonActionPerformed(ActionEvent evt) {
try {
ActionScript2Parser par = new ActionScript2Parser(mainPanel.getCurrentSwf().version);
ActionScript2Parser par = new ActionScript2Parser(mainPanel.getCurrentSwf());
src.setActions(par.actionsFromString(decompiledEditor.getText()));
SWF.uncache(src);
src.setModified();