diff --git a/build.properties b/build.properties index 77faf1f8f..5540c2fa3 100644 --- a/build.properties +++ b/build.properties @@ -1,5 +1,5 @@ # ---- Run parameters -run.params = +run.params = # ------------------------ diff --git a/lib/flashdebugger.jar b/lib/flashdebugger.jar new file mode 100644 index 000000000..8e5511090 Binary files /dev/null and b/lib/flashdebugger.jar differ 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 bbe5e538a..fb697a221 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -3095,13 +3095,13 @@ public final class SWF implements SWFContainerItem, Timelined { * * @param injectCode Modify AS3 code with debugfile / debugline ? */ - public void enableDebugging(boolean injectCode) { + public void enableDebugging(boolean injectCode, File decompileDir) { if (injectCode) { List packs = getAS3Packs(); for (ScriptPack s : packs) { if (s.isSimple) { - s.injectDebugInfo(); + s.injectDebugInfo(decompileDir); } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java index 135b50408..91130da18 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java @@ -52,6 +52,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -315,7 +316,7 @@ public class ScriptPack extends AS3ClassTreeItem { * Based on idea of Jacob Thompson * http://securityevaluators.com/knowledge/flash/ */ - public void injectDebugInfo() { + public void injectDebugInfo(File directoryPath) { Map> bodyToPosToLine = new HashMap<>(); try { CachedDecompilation decompiled = SWF.getCached(this); @@ -358,7 +359,7 @@ public class ScriptPack extends AS3ClassTreeItem { } int pos = -1; try { - abc.bodies.get(bodyIndex).getCode().adr2pos(instrOffset); + pos = abc.bodies.get(bodyIndex).getCode().adr2pos(instrOffset); } catch (ConvertException cex) { //ignore } @@ -375,7 +376,10 @@ public class ScriptPack extends AS3ClassTreeItem { Logger.getLogger(ScriptPack.class.getName()).log(Level.SEVERE, "Cannot decompile", ex); } - String filename = path.toString().replace('.', '/') + ".as"; + //String filepath = path.toString().replace('.', '/') + ".as"; + String pkg = path.packageStr.toString(); + String cls = path.className; + String filename = new File(directoryPath, path.packageStr.toFilePath()) + ";" + pkg + ";" + cls + ".as"; for (int bodyIndex : bodyToPosToLine.keySet()) { MethodBody b = abc.bodies.get(bodyIndex); @@ -383,11 +387,19 @@ public class ScriptPack extends AS3ClassTreeItem { List pos = new ArrayList<>(bodyToPosToLine.get(bodyIndex).keySet()); Collections.sort(pos); Collections.reverse(pos); + Set addedLines = new HashSet<>(); for (int i : pos) { int line = bodyToPosToLine.get(bodyIndex).get(i); + if (addedLines.contains(line)) { + continue; + } + addedLines.add(line); + Logger.getLogger(ScriptPack.class.getName()).log(Level.WARNING, "Script " + path + ": Insert debugline(" + line + ") at pos " + i + " to body " + bodyIndex); b.insertInstruction(i, new AVM2Instruction(0, AVM2Instructions.DebugLine, new int[]{line})); } + b.setModified(); } + ((Tag) abc.parentTag).setModified(true); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java index c59d9a1bc..a3e2b4b27 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java @@ -117,6 +117,10 @@ public final class MethodBody implements Cloneable { this.code = null; } + public void setModified() { + this.codeBytes = null; + } + public synchronized byte[] getCodeBytes() { if (codeBytes != null) { return codeBytes; diff --git a/nbproject/project.xml b/nbproject/project.xml index 4e901f120..286b09cc5 100644 --- a/nbproject/project.xml +++ b/nbproject/project.xml @@ -308,7 +308,7 @@ src - lib/LZMA.jar;lib/jna-3.5.1.jar;lib/jpproxy.jar;lib/trident-6.2.jar;lib/substance-flamingo-6.2.jar;lib/flamingo-6.2.jar;lib/substance-6.2.jar;lib/jl1.0.1.jar;lib/nellymoser.jar;lib/gif.jar;lib/avi.jar;lib/ttf.jar;lib/jpacker.jar;lib/sfntly.jar;lib/gnujpdf.jar;libsrc/ffdec_lib/src;lib/tablelayout.jar;lib/jsyntaxpane-0.9.5.jar;lib/JavactiveX.jar + lib/LZMA.jar;lib/jna-3.5.1.jar;lib/jpproxy.jar;lib/trident-6.2.jar;lib/substance-flamingo-6.2.jar;lib/flamingo-6.2.jar;lib/substance-6.2.jar;lib/jl1.0.1.jar;lib/nellymoser.jar;lib/gif.jar;lib/avi.jar;lib/ttf.jar;lib/jpacker.jar;lib/sfntly.jar;lib/gnujpdf.jar;libsrc/ffdec_lib/src;lib/tablelayout.jar;lib/jsyntaxpane-0.9.5.jar;lib/JavactiveX.jar;lib/flashdebugger.jar build javadoc reports diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index 2f1d30d06..7e6d5560f 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -2802,7 +2802,7 @@ public class CommandLineArgumentParser { FileInputStream fis = new FileInputStream(file); SWF swf = new SWF(fis, Configuration.parallelSpeedUp.get()); fis.close(); - swf.enableDebugging(injectas3); + swf.enableDebugging(injectas3, new File(outfile).getParentFile()); FileOutputStream fos = new FileOutputStream(outfile); swf.saveTo(fos); fos.close(); diff --git a/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java b/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java new file mode 100644 index 000000000..058f3f615 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2015 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.gui; + +import com.jpexs.debugger.flash.DebugConnectionListener; +import com.jpexs.debugger.flash.DebugMessageListener; +import com.jpexs.debugger.flash.Debugger; +import com.jpexs.debugger.flash.DebuggerCommands; +import com.jpexs.debugger.flash.DebuggerConnection; +import com.jpexs.debugger.flash.messages.in.InAskBreakpoints; +import com.jpexs.debugger.flash.messages.in.InBreakAt; +import com.jpexs.debugger.flash.messages.in.InNumScript; +import com.jpexs.debugger.flash.messages.in.InScript; +import com.jpexs.debugger.flash.messages.in.InSwfInfo; +import com.jpexs.decompiler.flash.abc.ClassPath; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.graph.DottedChain; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author Jindra + */ +public class DebuggerHandler implements DebugConnectionListener { + + @Override + public void connected(DebuggerConnection con) { + + Level level = Level.FINER; + + Logger rootLog = Logger.getLogger(Debugger.class.getName()); + rootLog.setLevel(level); + ConsoleHandler ch = new ConsoleHandler(); + ch.setLevel(level); + rootLog.addHandler(ch); + //rootLog.getHandlers()[0].setLevel(level); + + final DebuggerCommands dc = new DebuggerCommands(con); + dc.stopWarning(); + dc.setStopOnFault(); + dc.setEnumerateOverride(); + dc.setNotifyFailure(); + dc.setInvokeSetters(); + dc.setSwfLoadNotify(); + dc.setGetterTimeout(1500); + dc.setSetterTimeout(5000); + dc.squelch(true); + List swfs = dc.getSwfInfo(1); + int numScript = con.getMessage(InNumScript.class).num; + final Map moduleNames = new HashMap<>(); + for (int i = 0; i < numScript; i++) { + InScript sc = con.getMessage(InScript.class); + System.out.println("" + sc.module + ":" + sc.name); + moduleNames.put(sc.module, sc.name); + } + + final Map modulePaths = new HashMap<>(); + + for (int mname : moduleNames.keySet()) { + String name = moduleNames.get(mname); + String[] parts = name.split(";"); + + if (parts.length == 3) { + String clsName = parts[2].replace(".as", ""); + String pkg = parts[1]; + modulePaths.put(mname, new ClassPath(DottedChain.parse(pkg), clsName)); + } + } + + con.getMessage(InAskBreakpoints.class); + //dc.addBreakPoint(15, 14); + dc.addBreakPoint(9, 26); + con.addMessageListener(new DebugMessageListener() { + + @Override + public void message(InBreakAt message) { + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.WARNING, "break at {0}:{1}", new Object[]{moduleNames.get(message.file), message.line}); + String cls = modulePaths.get(message.file).toString(); + Main.getMainFrame().getPanel().debuggerBreakAt(Main.getMainFrame().getPanel().getCurrentSwf(), cls, message.line); + //dc.sendContinue(); + } + }); + dc.sendContinue(); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index 62af73636..6b41bd7fe 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -16,6 +16,16 @@ */ package com.jpexs.decompiler.flash.gui; +import com.jpexs.debugger.flash.DebugConnectionListener; +import com.jpexs.debugger.flash.DebugMessageListener; +import com.jpexs.debugger.flash.Debugger; +import com.jpexs.debugger.flash.DebuggerCommands; +import com.jpexs.debugger.flash.DebuggerConnection; +import com.jpexs.debugger.flash.messages.in.InAskBreakpoints; +import com.jpexs.debugger.flash.messages.in.InBreakAt; +import com.jpexs.debugger.flash.messages.in.InNumScript; +import com.jpexs.debugger.flash.messages.in.InScript; +import com.jpexs.debugger.flash.messages.in.InSwfInfo; import com.jpexs.decompiler.flash.ApplicationInfo; import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.SWF; @@ -79,8 +89,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; @@ -137,6 +149,8 @@ public class Main { public static boolean shouldCloseWhenClosingLoadingDialog; + private static Debugger flashDebugger; + public static void ensureMainFrame() { if (mainFrame == null) { synchronized (Main.class) { @@ -1001,6 +1015,22 @@ public class Main { public void onFinish(String clientId) { } }); + + try { + + /*Level level = Level.FINE; + + Logger rootLog = Logger.getLogger(""); + rootLog.setLevel(level); + rootLog.getHandlers()[0].setLevel(level); + */ + flashDebugger = new Debugger(); + flashDebugger.addConnectionListener(new DebuggerHandler()); + flashDebugger.start(); + } catch (IOException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } + } public static void showModeFrame() { @@ -1203,7 +1233,6 @@ public class Main { * @throws IOException On error */ public static void main(String[] args) throws IOException { - clearTemp(); try { diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 7605fc46a..f8a8bfbff 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -23,6 +23,7 @@ import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFBundle; import com.jpexs.decompiler.flash.SWFSourceInfo; import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ClassPath; import com.jpexs.decompiler.flash.abc.RenameType; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; @@ -1526,6 +1527,32 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } } + public void gotoClassLine(SWF swf, String cls, int line) { + gotoClass(swf, cls); + if (abcPanel != null) { + abcPanel.decompiledTextArea.selectLine(line); + } + } + + public void debuggerBreakAt(SWF swf, String cls, int line) { + gotoClassLine(swf, cls, line); + if (abcPanel != null) { + abcPanel.decompiledTextArea.setLineColor(line, Color.green); + } + } + + public void gotoClass(SWF swf, String cls) { + if (swf == null) { + return; + } + List abcList = swf.getAbcList(); + if (!abcList.isEmpty()) { + ABCPanel abcPanel = getABCPanel(); + abcPanel.setAbc(abcList.get(0).getABC()); + abcPanel.hilightScript(swf, cls); + } + } + public void gotoDocumentClass(SWF swf) { if (swf == null) { return; diff --git a/src/com/jpexs/decompiler/flash/gui/editor/LineMarkedEditorPane.java b/src/com/jpexs/decompiler/flash/gui/editor/LineMarkedEditorPane.java index 5199adb8a..427b3343c 100644 --- a/src/com/jpexs/decompiler/flash/gui/editor/LineMarkedEditorPane.java +++ b/src/com/jpexs/decompiler/flash/gui/editor/LineMarkedEditorPane.java @@ -32,6 +32,8 @@ import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.HashMap; +import java.util.Map; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.plaf.TextUI; @@ -65,6 +67,15 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan private LinkHandler linkHandler = this; + private Map lineColors = new HashMap<>(); + + public void setLineColor(int line, Color color) { + lineColors.remove(line); + if (color != null) { + lineColors.put(line, color); + } + } + public int getLine() { return lastLine; } @@ -358,10 +369,11 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan public void paint(Graphics g) { g.setColor(Color.white); g.fillRect(0, 0, getWidth(), getHeight()); + FontMetrics fontMetrics = g.getFontMetrics(); + int lh = fontMetrics.getHeight(); + int d = fontMetrics.getDescent(); + if (lastLine > 0) { - FontMetrics fontMetrics = g.getFontMetrics(); - int lh = fontMetrics.getHeight(); - int d = fontMetrics.getDescent(); if (error) { g.setColor(new Color(255, 200, 200)); } else { @@ -369,6 +381,10 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan } g.fillRect(0, d + lh * lastLine - 1, getWidth(), lh); } + for (int line : lineColors.keySet()) { + g.setColor(lineColors.get(line)); + g.fillRect(0, d + lh * line - 1, getWidth(), lh); + } super.paint(g); } }