diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index ed8feeada..7adb4190e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -794,12 +794,10 @@ public final class SWF implements SWFContainerItem, Timelined { ret[0] = 'Z'; } else if (compression == SWFCompression.ZLIB) { ret[0] = 'C'; + } else if (gfx) { + ret[0] = 'G'; } else { - if (gfx) { - ret[0] = 'G'; - } else { - ret[0] = 'F'; - } + ret[0] = 'F'; } if (gfx) { @@ -1480,10 +1478,8 @@ public final class SWF implements SWFContainerItem, Timelined { if (as3) { ret.addAll(new AS3ScriptExporter().exportActionScript3(this, handler, outdir, as3scripts, exportSettings, parallel, evl)); } - } else { - if (as2) { - ret.addAll(new AS2ScriptExporter().exportAS2Scripts(handler, outdir, getASMs(true), exportSettings, parallel, evl)); - } + } else if (as2) { + ret.addAll(new AS2ScriptExporter().exportAS2Scripts(handler, outdir, getASMs(true), exportSettings, parallel, evl)); } return ret; } @@ -1902,10 +1898,8 @@ public final class SWF implements SWFContainerItem, Timelined { TranslateStack brStack = (TranslateStack) stack.clone(); if (b >= 0) { getVariables(constantPool, localData, brStack, output, code, b, variables, functions, strings, visited, usageTypes, path); - } else { - if (debugMode) { - System.out.println("Negative branch:" + b); - } + } else if (debugMode) { + System.out.println("Negative branch:" + b); } } // } @@ -2020,12 +2014,10 @@ public final class SWF implements SWFContainerItem, Timelined { cnt += deobfuscateAS2Identifiers(renameType); cnt += deobfuscateAS3Identifiers(renameType); return cnt; + } else if (fileAttributes.actionScript3) { + return deobfuscateAS3Identifiers(renameType); } else { - if (fileAttributes.actionScript3) { - return deobfuscateAS3Identifiers(renameType); - } else { - return deobfuscateAS2Identifiers(renameType); - } + return deobfuscateAS2Identifiers(renameType); } } @@ -3060,16 +3052,14 @@ public final class SWF implements SWFContainerItem, Timelined { timelined.removeTag(tag); timelined.setModified(true); timelined.resetTimeline(); + } else // timeline should be always the swf here + if (removeDependencies) { + removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline()); + timelined.setModified(true); } else { - // timeline should be always the swf here - if (removeDependencies) { - removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline()); + boolean modified = removeTagFromTimeline(tag, timelined.getTimeline()); + if (modified) { timelined.setModified(true); - } else { - boolean modified = removeTagFromTimeline(tag, timelined.getTimeline()); - if (modified) { - timelined.setModified(true); - } } } } @@ -3121,14 +3111,11 @@ public final class SWF implements SWFContainerItem, Timelined { } /** - * Adds a tag to the SWF - * If targetTreeItem is: - * - Frame: adds the tag to the Frame. Frame can be a frame of the main - * timeline or a DefineSprite frame - * - DefineSprite: adds the tag to the end of the DefineSprite's tag list - * - Any other tag in the SWF: adds the new tag exactly before the specified - * tag - * - Other: adds the tag to the end of the SWF's tag list + * Adds a tag to the SWF If targetTreeItem is: - Frame: adds the tag to the + * Frame. Frame can be a frame of the main timeline or a DefineSprite frame + * - DefineSprite: adds the tag to the end of the DefineSprite's tag list - + * Any other tag in the SWF: adds the new tag exactly before the specified + * tag - Other: adds the tag to the end of the SWF's tag list * * @param tag * @param targetTreeItem diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionScriptFunction.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionScriptFunction.java index 9e919008b..d5771b5de 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionScriptFunction.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionScriptFunction.java @@ -15,6 +15,14 @@ public class ActionScriptFunction extends ActionScriptObject { protected List paramNames; protected Map funcRegNames; + public String getFunctionName() { + return functionName; + } + + public Map getFuncRegNames() { + return funcRegNames; + } + public ActionScriptFunction(long functionOffset, long functionLength, String functionName, List paramNames, Map funcRegNames) { this.functionOffset = functionOffset; this.functionLength = functionLength; @@ -23,11 +31,6 @@ public class ActionScriptFunction extends ActionScriptObject { this.funcRegNames = funcRegNames; } - public Object execute(Object thisObj, List args) { - //TODO!!! - return null; - } - public long getFunctionLength() { return functionLength; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/DisplayObject.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/DisplayObject.java index c8e4d286c..b44d71777 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/DisplayObject.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/DisplayObject.java @@ -1,6 +1,8 @@ package com.jpexs.decompiler.flash.action; +import com.jpexs.decompiler.flash.ecma.Undefined; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -54,6 +56,11 @@ public class DisplayObject extends ActionScriptObject { paused = false; } + public Object callFunction(long functionAddress, long functionLength, List args, Map regNames, Object thisObj) { + //TODO + return Undefined.INSTANCE; + } + public void callFrame(int frame) { //TODO } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/LocalDataArea.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/LocalDataArea.java index 3d3e8c85b..ed4f503ae 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/LocalDataArea.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/LocalDataArea.java @@ -35,7 +35,7 @@ public class LocalDataArea { public Stack stack = new Stack<>(); - public List functions = new ArrayList<>(); + public List functions = new ArrayList<>(); public Map localVariables = new HashMap<>(); @@ -70,6 +70,9 @@ public class LocalDataArea { constantPool = null; stack.clear(); localVariables.clear(); + localRegisters.clear(); + withs.clear(); + functions.clear(); jump = null; returnValue = null; executionException = null; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/special/ActionEnd.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/special/ActionEnd.java index 15cbafaf5..266237eeb 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/special/ActionEnd.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/special/ActionEnd.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.action.special; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.LocalDataArea; +import com.jpexs.decompiler.flash.ecma.Undefined; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.TranslateStack; @@ -47,6 +48,7 @@ public class ActionEnd extends Action { @Override public boolean execute(LocalDataArea lda) { + lda.returnValue = Undefined.INSTANCE; return true; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionCallFunction.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionCallFunction.java index b01d564a9..9801e51ca 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionCallFunction.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionCallFunction.java @@ -18,8 +18,11 @@ package com.jpexs.decompiler.flash.action.swf5; import com.jpexs.decompiler.flash.BaseLocalData; import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionScriptFunction; import com.jpexs.decompiler.flash.action.LocalDataArea; import com.jpexs.decompiler.flash.action.model.CallFunctionActionItem; +import com.jpexs.decompiler.flash.ecma.Null; +import com.jpexs.decompiler.flash.ecma.Undefined; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; @@ -47,6 +50,19 @@ public class ActionCallFunction extends Action { @Override public boolean execute(LocalDataArea lda) { + + String functionName = lda.popAsString(); + int numArgs = (int) (double) lda.popAsNumber(); + List args = new ArrayList<>(); + for (int i = 0; i < numArgs; i++) { + args.add(lda.pop()); + } + for (ActionScriptFunction f : lda.functions) { + if (functionName.equals(f.getFunctionName())) { + lda.stack.push(lda.stage.callFunction(f.getFunctionOffset(), f.getFunctionLength(), args, f.getFuncRegNames(), Undefined.INSTANCE /*?*/)); + return true; + } + } return true; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionCallMethod.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionCallMethod.java index ec78baaa1..30feaf3f1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionCallMethod.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionCallMethod.java @@ -62,7 +62,9 @@ public class ActionCallMethod extends Action { args.add(lda.pop()); } - lda.stack.push(((ActionScriptFunction) obj.getMember(methodName)).execute(obj, args)); + ActionScriptFunction f = (ActionScriptFunction) obj.getMember(methodName); + + lda.stack.push(lda.stage.callFunction(f.getFunctionOffset(), f.getFunctionLength(), args, f.getFuncRegNames(), obj)); return true; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionNewMethod.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionNewMethod.java index 9103179ed..fd9c94348 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionNewMethod.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionNewMethod.java @@ -62,8 +62,8 @@ public class ActionNewMethod extends Action { args.add(lda.pop()); } ActionScriptObject nobj = new ActionScriptObject(); - - ((ActionScriptFunction) obj.getMember(methodName)).execute(nobj, args); + ActionScriptFunction f = (ActionScriptFunction) obj.getMember(methodName); + lda.stage.callFunction(f.getFunctionOffset(), f.getFunctionLength(), args, f.getFuncRegNames(), nobj); lda.stack.push(nobj); return true; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionNewObject.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionNewObject.java index d95328632..ec9a4df87 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionNewObject.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionNewObject.java @@ -63,7 +63,7 @@ public class ActionNewObject extends Action { ActionScriptObject obj = new ActionScriptObject(); //TODO:check type ActionScriptFunction constructor = (ActionScriptFunction) lda.stage.getMember(objectName); - constructor.execute(obj, args); + lda.stage.callFunction(constructor.getFunctionOffset(), constructor.getFunctionLength(), args, constructor.getFuncRegNames(), obj); lda.stack.push(obj); return true; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java index 32e74936e..ae7bd659d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -543,6 +543,11 @@ public class Configuration { @ConfigurationCategory("import") public static final ConfigurationItem shapeImportUseNonSmoothedFill = null; + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("display") + @ConfigurationName("internalFlashViewer.execute.as12") + public static final ConfigurationItem internalFlashViewerExecuteAs12 = null; + private enum OSId { WINDOWS, OSX, UNIX diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index bdeb98cea..801dbbd50 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -17,11 +17,18 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.LocalDataArea; +import com.jpexs.decompiler.flash.action.Stage; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.ecma.Undefined; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.gui.player.MediaDisplay; import com.jpexs.decompiler.flash.gui.player.MediaDisplayListener; import com.jpexs.decompiler.flash.gui.player.Zoom; import com.jpexs.decompiler.flash.tags.DefineButtonSoundTag; +import com.jpexs.decompiler.flash.tags.DoActionTag; import com.jpexs.decompiler.flash.tags.base.BoundedTag; import com.jpexs.decompiler.flash.tags.base.ButtonTag; import com.jpexs.decompiler.flash.tags.base.CharacterTag; @@ -36,6 +43,7 @@ import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.ConstantColorColorTransform; import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.helpers.ByteArrayRange; import com.jpexs.helpers.SerializableImage; import java.awt.AlphaComposite; import java.awt.BasicStroke; @@ -57,6 +65,7 @@ import java.awt.image.BufferedImage; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.logging.Level; @@ -86,6 +95,8 @@ public final class ImagePanel extends JPanel implements MediaDisplay { private boolean loop; + private LocalDataArea lda; + private boolean zoomAvailable = false; private SWF swf; @@ -522,6 +533,61 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } public void setTimelined(final Timelined drawable, final SWF swf, int frame) { + Stage stage = new Stage() { + @Override + public void callFrame(int frame) { + executeFrame(frame); + } + + @Override + public Object callFunction(long functionAddress, long functionLength, List args, Map regNames, Object thisObj) { + try { + SWFInputStream sis = new SWFInputStream(swf, swf.uncompressedData, functionAddress, (int) (functionAddress + functionLength)); + return execute(sis); + } catch (IOException ex) { + Logger.getLogger(ImagePanel.class.getName()).log(Level.SEVERE, null, ex); + } + return Undefined.INSTANCE; + } + + @Override + public int getCurrentFrame() { + return ImagePanel.this.getCurrentFrame(); + } + + @Override + public int getTotalFrames() { + return ImagePanel.this.getTotalFrames(); + } + + @Override + public void gotoFrame(int frame) { + ImagePanel.this.pause(); + ImagePanel.this.gotoFrame(frame); + } + + @Override + public void gotoLabel(String label) { + //TODO + } + + @Override + public void pause() { + ImagePanel.this.pause(); + } + + @Override + public void play() { + ImagePanel.this.play(); + } + + @Override + public void trace(Object... val) { + //TODO + } + + }; + lda = new LocalDataArea(stage); synchronized (ImagePanel.class) { stopInternal(); if (drawable instanceof ButtonTag) { @@ -571,6 +637,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } public synchronized void setImage(SerializableImage image) { + lda = null; setBackground(View.getSwfBackgroundColor()); clear(); @@ -589,6 +656,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { setBackground(View.getSwfBackgroundColor()); clear(); + lda = null; timelined = null; loaded = true; stillFrame = true; @@ -783,6 +851,54 @@ public final class ImagePanel extends JPanel implements MediaDisplay { return img; } + private Object execute(SWFInputStream sis) throws IOException { + if (!Configuration.internalFlashViewerExecuteAs12.get()) { + return Undefined.INSTANCE; + } + if (lda == null) { + return Undefined.INSTANCE; + } + Action a; + while ((a = sis.readAction()) != null) { + a.setAddress(sis.getPos()); + a.execute(lda); + if (lda.returnValue != null) { + return lda.returnValue; + } + if (lda.jump != null) { + sis.seek(lda.jump); + } + } + return Undefined.INSTANCE; + } + + private void executeFrame(int frame) { + if (!Configuration.internalFlashViewerExecuteAs12.get()) { + return; + } + if (timelined == null) { + return; + } + List actions = timelined.getTimeline().getFrame(frame).actions; + if (lda != null) { + lda.clear(); + } + for (DoActionTag src : actions) { + try { + ByteArrayRange actionBytes = src.getActionBytes(); + int prevLength = actionBytes.getPos(); + SWFInputStream rri = new SWFInputStream(swf, actionBytes.getArray()); + if (prevLength != 0) { + rri.seek(prevLength); + } + execute(rri); + } catch (IOException ex) { + Logger.getLogger(ImagePanel.class.getName()).log(Level.SEVERE, null, ex); + } + + } + } + private void drawFrame(Timer thisTimer) { Timelined timelined; MouseEvent lastMouseEvent; @@ -849,6 +965,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { playSound(st, thisTimer); } } + executeFrame(frame); } catch (Throwable ex) { // swf was closed during the rendering probably return; diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties index 186bff973..2fb1b6998 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties @@ -411,3 +411,6 @@ config.description.warning.svgImport = config.name.shapeImport.useNonSmoothedFill = Use non-smoothed fill when a shape is replaced with an image config.description.shapeImport.useNonSmoothedFill = + +config.name.internalFlashViewer.execute.as12 = AS1/2 in own flash viewer (Experimental) +config.description.internalFlashViewer.execute.as12 = Try to execute ActionScript 1/2 during SWF playback using FFDec flash viewer \ No newline at end of file diff --git a/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java b/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java index 7e4ccee91..e8e1fcd7d 100644 --- a/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java +++ b/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java @@ -28,6 +28,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.ActionLocalData; import com.jpexs.decompiler.flash.action.LocalDataArea; +import com.jpexs.decompiler.flash.action.Stage; import com.jpexs.decompiler.flash.action.swf4.ActionAdd; import com.jpexs.decompiler.flash.action.swf4.ActionAnd; import com.jpexs.decompiler.flash.action.swf4.ActionAsciiToChar; @@ -488,7 +489,7 @@ public class FlashPlayerTest { String ffdecExecuteResult; try { - LocalDataArea lda = new LocalDataArea(); + LocalDataArea lda = new LocalDataArea(new Stage()); for (Action a : newActions) { if (!a.execute(lda)) { fail(); @@ -538,7 +539,7 @@ public class FlashPlayerTest { } }*/ - /*if (!ffdecResult.equals(flashResult)) { + /*if (!ffdecResult.equals(flashResult)) { LocalDataArea lda = new LocalDataArea(); for (Action a : task.actions) { if (!a.execute(lda)) {