diff --git a/.gitignore b/.gitignore index 35c466825..613ec67bd 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.conflict~ *.identcache *.recompiled.swf +run_test_*.swf Thumbs.db /build/ /reports/ diff --git a/lib/flashdebugger.jar b/lib/flashdebugger.jar index 7c75988c3..929006310 100644 Binary files a/lib/flashdebugger.jar and b/lib/flashdebugger.jar differ diff --git a/libsrc/ffdec_lib/nbproject/project.xml b/libsrc/ffdec_lib/nbproject/project.xml index 029f090fb..1a2e9209f 100644 --- a/libsrc/ffdec_lib/nbproject/project.xml +++ b/libsrc/ffdec_lib/nbproject/project.xml @@ -52,7 +52,7 @@ auxiliary.show.customizer.message= clean build - + debug-nb @@ -137,7 +137,7 @@ auxiliary.show.customizer.message= - + @@ -147,7 +147,7 @@ auxiliary.show.customizer.message= test \.java$ java-name - + @@ -181,7 +181,7 @@ auxiliary.show.customizer.message= - + folder @@ -219,7 +219,7 @@ auxiliary.show.customizer.message= nbproject/ide-file-targets.xml - + @@ -236,7 +236,7 @@ auxiliary.show.customizer.message= src - ../../lib/LZMA.jar;../../lib/avi.jar;../../lib/gif.jar;../../lib/gnujpdf.jar;../../lib/jl1.0.1.jar;../../lib/jpacker.jar;../../lib/nellymoser.jar;../../lib/sfntly.jar;../../lib/ttf.jar;../../lib/cmykjpeg.jar;../../src + ../../lib/LZMA.jar;../../lib/avi.jar;../../lib/gif.jar;../../lib/gnujpdf.jar;../../lib/jl1.0.1.jar;../../lib/jpacker.jar;../../lib/nellymoser.jar;../../lib/sfntly.jar;../../lib/ttf.jar;../../lib/cmykjpeg.jar;../../src;../../lib/flashdebugger.jar build reports dist 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 2797cdd27..b79a64927 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash; import SevenZip.Compression.LZMA.Decoder; import SevenZip.Compression.LZMA.Encoder; +import com.jpexs.debugger.flash.SWD; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.CachedDecompilation; import com.jpexs.decompiler.flash.abc.ClassPath; @@ -77,6 +78,7 @@ import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; import com.jpexs.decompiler.flash.helpers.collections.MyEntry; import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.DebugIDTag; import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; import com.jpexs.decompiler.flash.tags.DefineSpriteTag; import com.jpexs.decompiler.flash.tags.DoInitActionTag; @@ -157,6 +159,7 @@ import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -171,10 +174,15 @@ import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.Stack; +import java.util.TreeMap; +import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; @@ -1094,12 +1102,7 @@ public final class SWF implements SWFContainerItem, Timelined { } } - /*preload shape tags - for (Tag tag : tags) { - if (tag instanceof ShapeTag) { - ((ShapeTag) tag).getShapes(); - } - }*/ + getASMs(true); // Add scriptNames to ASMs } @Override @@ -1473,11 +1476,14 @@ public final class SWF implements SWFContainerItem, Timelined { TreeItem realItem = treeItem instanceof TagScript ? ((TagScript) treeItem).getTag() : treeItem; if (realItem instanceof ASMSource && (exportAll || exportNode)) { String npath = path; + String exPath = path; int ppos = 1; while (asmsToExport.containsKey(npath)) { ppos++; npath = path + (exportFileNames ? "[" + ppos + "]" : "_" + ppos); + exPath = path + "[" + ppos + "]"; } + ((ASMSource) realItem).setScriptName(exPath); asmsToExport.put(npath, (ASMSource) realItem); } @@ -3143,6 +3149,8 @@ public final class SWF implements SWFContainerItem, Timelined { int pos = 0; + boolean hasEnabled = false; + for (int i = 0; i < tags.size(); i++) { Tag t = tags.get(i); if (t instanceof MetadataTag) { @@ -3152,23 +3160,183 @@ public final class SWF implements SWFContainerItem, Timelined { pos = i + 1; } if (version >= 6 && (t instanceof EnableDebugger2Tag)) { - return; + hasEnabled = true; + break; } if (version == 5 && (t instanceof EnableDebuggerTag)) { - return; + hasEnabled = true; + break; } if (version < 5 && (t instanceof ProtectTag)) { - return; + hasEnabled = true; + break; } } - if (version >= 6) { - tags.add(pos, new EnableDebugger2Tag(this)); - } else if (version == 5) { - tags.add(pos, new EnableDebuggerTag(this)); - } else { - tags.add(pos, new ProtectTag(this)); + if (!hasEnabled) { + if (version >= 6) { + tags.add(pos, new EnableDebugger2Tag(this)); + } else if (version == 5) { + tags.add(pos, new EnableDebuggerTag(this)); + } else { + tags.add(pos, new ProtectTag(this)); + } } + + addDebugId(); + } + + public DebugIDTag getDebugId() { + for (Tag t : tags) { + if (t instanceof DebugIDTag) { + return (DebugIDTag) t; + } + } + return null; + } + + public DebugIDTag addDebugId() { + DebugIDTag r = getDebugId(); + if (r == null) { + for (int i = 0; i < tags.size(); i++) { + Tag t = tags.get(i); + if ((t instanceof EnableDebuggerTag) || (t instanceof EnableDebugger2Tag)) { + r = new DebugIDTag(this); + tags.add(i + 1, r); + new Random().nextBytes(r.debugId); + break; + } + } + } + return r; + } + + public boolean generateSwdFile(File file, Map> breakpoints) throws IOException { + DebugIDTag dit = getDebugId(); + if (dit == null) { + return false; + } + List items = new ArrayList<>(); + Map asms = getASMs(true); + + try { + items.add(new SWD.DebugId(dit.debugId)); + Random rnd = new Random(); + + //Map moduleIds = new HashMap<>(); + List swdOffsets = new ArrayList<>(); + List swfBps = new ArrayList<>(); + int moduleId = 0; + List names = new ArrayList<>(asms.keySet()); + Collections.sort(names); + //Collections.reverse(names); + for (String name : names) { + moduleId++; + CachedScript cs; + try { + cs = SWF.getCached(asms.get(name), asms.get(name).getActions()); + } catch (InterruptedException ex) { + return false; + } + String txt = cs.text.replace("\r", ""); + int line = 1; + Map lineToOffset = new HashMap<>(); + Map regNames = new HashMap<>(); + + for (int pos = 0; pos < txt.length(); pos++) { + Highlighting h = Highlighting.searchPos(cs.hilights, pos); + if (h != null) { + + int firstLineOffset = (int) h.getProperties().firstLineOffset; + if (firstLineOffset != -1) { + if (h.getProperties().declaration && h.getProperties().regIndex > -1) { + regNames.put(h.getProperties().regIndex, h.getProperties().localName); + + /*List curRegIndexes = new ArrayList<>(regNames.keySet()); + List curRegNames = new ArrayList<>(); + for (int i = 0; i < curRegIndexes.size(); i++) { + curRegNames.add(regNames.get(i)); + } + items.add(new SWD.DebugRegisters((int) h.getProperties().firstLineOffset, curRegIndexes, curRegNames));*/ + } + } + if (firstLineOffset != -1 && !lineToOffset.containsKey(line)) { + lineToOffset.put(line, firstLineOffset); + } + } + if (txt.charAt(pos) == '\n') { + line++; + } + } + + Map offSetToLine = new TreeMap<>(); + for (Map.Entry en : lineToOffset.entrySet()) { + offSetToLine.put(en.getValue(), en.getKey()); + } + + //final String NONAME = "[No instance name assigned]"; + String sname = name; + int bitmap = SWD.bitmapAction; + /* Matcher m; + int bitmap = SWD.bitmapAction; + m = Pattern.compile("^\\\\frame_([0-9]+)\\\\DoAction$").matcher(sname); + if (m.matches()) { + //TODO: scenes?, layers? + sname = "Actions for Scene 1: Frame " + m.group(1) + " of Layer Name Layer 1"; + } else if ((m = Pattern.compile("^\\\\__Packages\\\\(.*)$").matcher(sname)).matches()) { + sname = m.group(1).replace("\\", ".") + ": .\\" + m.group(1) + ".as"; + } else { + continue; //FIXME! + } + m = Pattern.compile("^\\\\DefineSprite_([0-9])+\\\\frame_([0-9]+)\\\\DoAction$").matcher(sname); + if (m.matches()) { + //TODO: layers? + //sname = "Actions for Symbol " + m.group(1) + ": Frame " + m.group(2) + " of Layer Name Layer 1"; + continue; //FIXME! + } + + //TODO: handle onxxx together ? + m = Pattern.compile("^\\\\DefineButton2?_([0-9]+)\\\\on\\(.*$").matcher(sname); + if (m.matches()) { + //bitmap = SWD.bitmapOnAction; + //sname = "Actions for " + NONAME + " (Symbol " + m.group(1) + ")"; + continue; //FIXME! + } + + //TODO: handle onClipEvent together ? + m = Pattern.compile("^\\\\frame_([0-9]+)\\\\PlaceObject[2-3]?_([0-9]+)_[^\\\\]*\\\\onClipEvent\\(.*$").matcher(sname); + if (m.matches()) { + //bitmap = SWD.bitmapOnClipAction; + //sname = "Actions for " + NONAME + " (Symbol " + m.group(2) + ")"; + continue; //FIXME! + }//*/ + + items.add(new SWD.DebugScript(moduleId, bitmap, sname, txt)); + for (int ofs : offSetToLine.keySet()) { + items.add(new SWD.DebugOffset(moduleId, offSetToLine.get(ofs), ofs)); + } + if (breakpoints.containsKey(name)) { + Set bplines = breakpoints.get(name); + for (int bpline : bplines) { + if (lineToOffset.containsKey(bpline)) { + items.add(new SWD.DebugBreakpoint(moduleId, bpline)); + } + } + } + //moduleId++; + } + //items.addAll(swdOffsets); + //items.addAll(swfBps); + + } catch (Throwable t) { + Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "message", t); + return false; + } + SWD swd = new SWD(7, items); + try (FileOutputStream fis = new FileOutputStream(file)) { + swd.saveTo(fis); + } + return true; } public boolean enableTelemetry(String password) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java index 04e61554a..3a937aeb6 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java @@ -897,7 +897,7 @@ public class SWFInputStream implements AutoCloseable { InflaterInputStream dis = new InflaterInputStream(new ByteArrayInputStream(data, offset, length)); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buf = new byte[4096]; - int c = 0; + int c; while ((c = dis.read(buf)) > 0) { baos.write(buf, 0, c); } @@ -1202,11 +1202,7 @@ public class SWFInputStream implements AutoCloseable { break; case DoActionTag.ID: case DoInitActionTag.ID: - if (isAS3) { - doParse = false; - } else { - doParse = true; - } + doParse = !isAS3; break; case ShowFrameTag.ID: case PlaceObjectTag.ID: @@ -1233,7 +1229,7 @@ public class SWFInputStream implements AutoCloseable { } } - if (parseTags && doParse && parallel1 && tag instanceof TagStub) { + if (parseTags && doParse && parallel1 && tag instanceof TagStub && executor != null) { Future future = executor.submit(new TagResolutionTask((TagStub) tag, di, level, parallel1, skipUnusualTags, lazy)); futureResults.add(future); } else { @@ -1262,7 +1258,9 @@ public class SWFInputStream implements AutoCloseable { } } - executor.shutdown(); + if (executor != null) { + executor.shutdown(); + } } return tags; } @@ -1670,7 +1668,7 @@ public class SWFInputStream implements AutoCloseable { * @throws IOException */ public Action readAction() throws IOException { - int actionCode = -1; + int actionCode; try { actionCode = readUI8("actionCode"); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java index 785a68386..36a84714d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java @@ -61,6 +61,11 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem { private String file; + @Override + public long getLineOffset() { + return getOffset(); + } + public void setFileLine(String file, int line) { this.file = file; this.line = line; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java index b87cd8a81..30aed124c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java @@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.BaseLocalData; import com.jpexs.decompiler.flash.DisassemblyListener; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; import com.jpexs.decompiler.flash.action.deobfuscation.ActionDeobfuscator; import com.jpexs.decompiler.flash.action.model.ActionItem; import com.jpexs.decompiler.flash.action.model.ConstantPool; @@ -109,6 +110,8 @@ public abstract class Action implements GraphSourceItem { private boolean ignored = false; + public long fileOffset = -1; + /** * Action type identifier */ @@ -121,6 +124,11 @@ public abstract class Action implements GraphSourceItem { private long address; + @Override + public long getLineOffset() { + return fileOffset; + } + /** * Names of ActionScript properties */ @@ -937,11 +945,12 @@ public abstract class Action implements GraphSourceItem { this.ignored = ignored; } - public static List actionsPartToTree(HashMap registerNames, HashMap variables, HashMap functions, TranslateStack stack, List actions, int start, int end, int version, int staticOperation, String path) throws InterruptedException { + public static List actionsPartToTree(Reference fi, HashMap registerNames, HashMap variables, HashMap functions, TranslateStack stack, List actions, int start, int end, int version, int staticOperation, String path) throws InterruptedException { if (start < actions.size() && (end > 0) && (start > 0)) { logger.log(Level.FINE, "Entering {0}-{1}{2}", new Object[]{start, end, actions.size() > 0 ? (" (" + actions.get(start).toString() + " - " + actions.get(end == actions.size() ? end - 1 : end) + ")") : ""}); } ActionLocalData localData = new ActionLocalData(registerNames, variables, functions); + localData.lineStartAction = fi.getVal(); List output = new ArrayList<>(); int ip = start; boolean isWhile = false; @@ -964,6 +973,10 @@ public abstract class Action implements GraphSourceItem { ip++; continue; } + if (stack.isEmpty()) { + localData.lineStartAction = action; + fi.setVal(action); + } if (action instanceof GraphSourceItemContainer) { GraphSourceItemContainer cnt = (GraphSourceItemContainer) action; //List out=actionsPartToTree(new HashMap(), new HashMap(),new HashMap(), new TranslateStack(), src, ip+1,endip-1 , version); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java index 6736501de..70f0fe180 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java @@ -79,7 +79,11 @@ public class ActionGraphSource extends GraphSource { @Override public List translatePart(GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException { - return Action.actionsPartToTree(registerNames, variables, functions, stack, actions, start, end, version, staticOperation, path); + Reference fi = new Reference<>(localData.lineStartInstruction); + + List r = Action.actionsPartToTree(fi, registerNames, variables, functions, stack, actions, start, end, version, staticOperation, path); + localData.lineStartInstruction = fi.getVal(); + return r; } private List posCache = null; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionListReader.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionListReader.java index 09477c455..0dabf6967 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionListReader.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionListReader.java @@ -796,6 +796,7 @@ public class ActionListReader { if ((a = sis.readAction()) == null) { break; } + a.fileOffset = ip; int actionLengthWithHeader = a.getTotalActionLength(); 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 3b095a479..260d37d76 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 @@ -524,6 +524,10 @@ public class Configuration { @ConfigurationName("gui.avm2.splitPane.vars.dividerLocationPercent") public static final ConfigurationItem guiAvm2VarsSplitPaneDividerLocationPercent = null; + @ConfigurationDefaultDouble(0.7) + @ConfigurationName("gui.action.splitPane.vars.dividerLocationPercent") + public static final ConfigurationItem guiActionVarsSplitPaneDividerLocationPercent = null; + @ConfigurationDefaultBoolean(true) @ConfigurationCategory("script") public static final ConfigurationItem debugHalt = null; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/HighlightedTextWriter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/HighlightedTextWriter.java index f323423ff..3c095c8c4 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/HighlightedTextWriter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/HighlightedTextWriter.java @@ -184,7 +184,7 @@ public class HighlightedTextWriter extends GraphTextWriter { ndata.merge(data); ndata.offset = src.getOffset() + pos; if (itemPos.startLineItem != null) { - ndata.firstLineOffset = itemPos.startLineItem.getOffset(); + ndata.firstLineOffset = itemPos.startLineItem.getLineOffset(); } h = new Highlighting(sb.length() - newLineCount, ndata, HighlightType.OFFSET, str); instructionHilights.add(h); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java index 7614aa493..696baf0ed 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java @@ -84,6 +84,13 @@ public class DefineButtonTag extends ButtonTag implements ASMSource { private boolean isSingleFrame; + private String scriptName = "-"; + + @Override + public String getScriptName() { + return scriptName; + } + /** * Constructor * @@ -108,6 +115,11 @@ public class DefineButtonTag extends ButtonTag implements ASMSource { readData(sis, data, 0, false, false, false); } + @Override + public void setScriptName(String scriptName) { + this.scriptName = scriptName; + } + @Override public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { buttonId = sis.readUI16("buttonId"); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoActionTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoActionTag.java index 016b4ba0a..81d746019 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoActionTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoActionTag.java @@ -27,6 +27,7 @@ import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.tags.base.ASMSource; import com.jpexs.decompiler.flash.types.annotations.HideInRawEdit; +import com.jpexs.decompiler.flash.types.annotations.Internal; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.helpers.ByteArrayRange; import com.jpexs.helpers.Helper; @@ -54,6 +55,14 @@ public class DoActionTag extends Tag implements ASMSource { @HideInRawEdit public ByteArrayRange actionBytes; + @Internal + private String scriptName = "-"; + + @Override + public String getScriptName() { + return scriptName; + } + /** * Constructor * @@ -87,6 +96,11 @@ public class DoActionTag extends Tag implements ASMSource { readData(sis, data, 0, false, false, false); } + @Override + public void setScriptName(String scriptName) { + this.scriptName = scriptName; + } + @Override public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { actionBytes = sis.readByteRangeEx(sis.available(), "actionBytes"); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java index 306070656..488529b56 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java @@ -29,6 +29,7 @@ import com.jpexs.decompiler.flash.tags.base.ASMSource; import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.annotations.HideInRawEdit; +import com.jpexs.decompiler.flash.types.annotations.Internal; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.helpers.ByteArrayRange; @@ -61,6 +62,14 @@ public class DoInitActionTag extends Tag implements CharacterIdTag, ASMSource { @HideInRawEdit public ByteArrayRange actionBytes; + @Internal + private String scriptName = "-"; + + @Override + public String getScriptName() { + return scriptName; + } + /** * Constructor * @@ -71,6 +80,11 @@ public class DoInitActionTag extends Tag implements CharacterIdTag, ASMSource { actionBytes = ByteArrayRange.EMPTY; } + @Override + public void setScriptName(String scriptName) { + this.scriptName = scriptName; + } + /** * Constructor * diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java index 13bacc8b7..555aab836 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java @@ -91,4 +91,8 @@ public interface ASMSource extends Exportable { public Tag getSourceTag(); public void setSourceTag(Tag t); + + public String getScriptName(); + + public void setScriptName(String scriptName); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java index 18088e6c9..02d1caa71 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java @@ -47,6 +47,13 @@ public class BUTTONCONDACTION implements ASMSource, Serializable { private Tag tag; + private String scriptName = "-"; + + @Override + public String getScriptName() { + return scriptName; + } + // Constructor for Generic tag editor. public BUTTONCONDACTION() { swf = null; @@ -54,6 +61,11 @@ public class BUTTONCONDACTION implements ASMSource, Serializable { actionBytes = new ByteArrayRange(SWFInputStream.BYTE_ARRAY_EMPTY); } + @Override + public void setScriptName(String scriptName) { + this.scriptName = scriptName; + } + public BUTTONCONDACTION(SWF swf, SWFInputStream sis, Tag tag) throws IOException { this.swf = swf; this.tag = tag; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java index 8bbddace8..af7f093cc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java @@ -42,6 +42,13 @@ import java.util.List; */ public class CLIPACTIONRECORD implements ASMSource, Serializable { + private String scriptName = "-"; + + @Override + public String getScriptName() { + return scriptName; + } + public static String keyToString(int key) { if ((key < CLIPACTIONRECORD.KEYNAMES.length) && (key > 0) && (CLIPACTIONRECORD.KEYNAMES[key] != null)) { return CLIPACTIONRECORD.KEYNAMES[key]; @@ -100,6 +107,11 @@ public class CLIPACTIONRECORD implements ASMSource, Serializable { actionBytes = ByteArrayRange.EMPTY; } + @Override + public void setScriptName(String scriptName) { + this.scriptName = scriptName; + } + public CLIPACTIONRECORD(SWF swf, SWFInputStream sis, Tag tag) throws IOException { this.swf = swf; this.tag = tag; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSourceItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSourceItem.java index eb22fd127..7436dfb8c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSourceItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSourceItem.java @@ -37,6 +37,8 @@ public interface GraphSourceItem extends Serializable, Cloneable { public long getOffset(); + public long getLineOffset(); + public boolean ignoredLoops(); public List getBranches(GraphSource code); diff --git a/libsrc/ffdec_lib/testdata/run_as2/run_as2.swf b/libsrc/ffdec_lib/testdata/run_as2/run_as2.swf new file mode 100644 index 000000000..38b3016c5 Binary files /dev/null and b/libsrc/ffdec_lib/testdata/run_as2/run_as2.swf differ diff --git a/libsrc/ffdec_lib/testdata/run_as3/run.swf b/libsrc/ffdec_lib/testdata/run_as3/run.swf index 4dfc98abd..c0c7e713d 100644 Binary files a/libsrc/ffdec_lib/testdata/run_as3/run.swf and b/libsrc/ffdec_lib/testdata/run_as3/run.swf differ diff --git a/src/com/jpexs/decompiler/flash/gui/abc/DebugPanel.java b/src/com/jpexs/decompiler/flash/gui/DebugPanel.java similarity index 87% rename from src/com/jpexs/decompiler/flash/gui/abc/DebugPanel.java rename to src/com/jpexs/decompiler/flash/gui/DebugPanel.java index d18a8ba53..3f9d334c0 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/DebugPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/DebugPanel.java @@ -14,17 +14,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.jpexs.decompiler.flash.gui.abc; +package com.jpexs.decompiler.flash.gui; import com.jpexs.debugger.flash.Variable; import com.jpexs.debugger.flash.messages.in.InBreakAtExt; import com.jpexs.debugger.flash.messages.in.InFrame; import com.jpexs.decompiler.flash.gui.AppStrings; import com.jpexs.decompiler.flash.gui.DebuggerHandler; -import com.jpexs.decompiler.flash.gui.DebuggerHandler.VariableChangedListener; +import com.jpexs.decompiler.flash.gui.DebuggerHandler.BreakListener; import com.jpexs.decompiler.flash.gui.HeaderLabel; import com.jpexs.decompiler.flash.gui.Main; import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.abc.ABCPanel; import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowLayout; @@ -55,16 +56,25 @@ public class DebugPanel extends JPanel { private JTable callStackTable; private JTable stackTable; private JTabbedPane varTabs; - private VariableChangedListener listener; + private BreakListener listener; private JTextArea traceLogTextarea; private int logLength = 0; private List tabTypes = new ArrayList<>(); + private boolean loading = false; public static enum SelectedTab { LOG, STACK, SCOPECHAIN, LOCALS, REGISTERS, CALLSTACK } + public synchronized boolean isLoading() { + return loading; + } + + public synchronized void setLoading(boolean loading) { + this.loading = loading; + } + private SelectedTab selectedTab = null; public DebugPanel() { @@ -101,11 +111,30 @@ public class DebugPanel extends JPanel { } }); - Main.getDebugHandler().addVariableChangedListener(listener = new DebuggerHandler.VariableChangedListener() { + Main.getDebugHandler().addBreakListener(listener = new DebuggerHandler.BreakListener() { @Override - public void variablesChanged() { - refresh(); + public void doContinue() { + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + refresh(); + } + + }); + } + + @Override + public void breakAt(String scriptName, int line) { + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + refresh(); + } + }); + } }); @@ -115,6 +144,9 @@ public class DebugPanel extends JPanel { @Override public void stateChanged(ChangeEvent e) { if (e.getSource() == varTabs) { + if (isLoading()) { + return; + } synchronized (DebugPanel.this) { int si = varTabs.getSelectedIndex(); if (si > -1 && si < tabTypes.size()) { @@ -135,10 +167,10 @@ public class DebugPanel extends JPanel { @Override public void run() { + setLoading(true); synchronized (DebugPanel.this) { + SelectedTab oldSel = selectedTab; - SelectedTab firstVisible = null; - SelectedTab newSel = null; InFrame f = Main.getDebugHandler().getFrame(); if (f != null) { debugRegistersTable.setModel(new ABCPanel.VariablesTableModel(f.registers)); @@ -251,23 +283,24 @@ public class DebugPanel extends JPanel { setVisible(newVisible); } if (!tabTypes.isEmpty()) { - if (oldSel != null && tabTypes.contains(oldSel)) { - selectedTab = oldSel; - } else { - selectedTab = tabTypes.get(0); + if (oldSel != null && !tabTypes.contains(oldSel)) { + oldSel = null; } - varTabs.setSelectedIndex(tabTypes.indexOf(selectedTab)); - } else { - selectedTab = null; } + if (oldSel != null) { + selectedTab = oldSel; + varTabs.setSelectedIndex(tabTypes.indexOf(selectedTab)); + } + setLoading(false); } + } }); } public void dispose() { - Main.getDebugHandler().removeVariableChangedListener(listener); + Main.getDebugHandler().removeBreakListener(listener); } } diff --git a/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java b/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java index 265e25caa..6b12b847f 100644 --- a/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java +++ b/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java @@ -21,6 +21,7 @@ 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.SWD; import com.jpexs.debugger.flash.Variable; import com.jpexs.debugger.flash.messages.in.InAskBreakpoints; import com.jpexs.debugger.flash.messages.in.InBreakAt; @@ -28,13 +29,19 @@ import com.jpexs.debugger.flash.messages.in.InBreakAtExt; import com.jpexs.debugger.flash.messages.in.InBreakReason; import com.jpexs.debugger.flash.messages.in.InContinue; import com.jpexs.debugger.flash.messages.in.InFrame; +import com.jpexs.debugger.flash.messages.in.InGetSwd; +import com.jpexs.debugger.flash.messages.in.InGetSwf; import com.jpexs.debugger.flash.messages.in.InNumScript; +import com.jpexs.debugger.flash.messages.in.InProcessTag; import com.jpexs.debugger.flash.messages.in.InScript; import com.jpexs.debugger.flash.messages.in.InSetBreakpoint; import com.jpexs.debugger.flash.messages.in.InSwfInfo; import com.jpexs.debugger.flash.messages.in.InTrace; import com.jpexs.debugger.flash.messages.in.InVersion; import com.jpexs.debugger.flash.messages.out.OutGetBreakReason; +import com.jpexs.debugger.flash.messages.out.OutGetSwd; +import com.jpexs.debugger.flash.messages.out.OutGetSwf; +import com.jpexs.debugger.flash.messages.out.OutProcessedTag; import com.jpexs.decompiler.flash.abc.ClassPath; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.configuration.Configuration; @@ -43,6 +50,7 @@ import com.jpexs.helpers.CancellableWorker; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -50,6 +58,8 @@ import java.util.TreeSet; import java.util.logging.ConsoleHandler; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * @@ -61,41 +71,178 @@ public class DebuggerHandler implements DebugConnectionListener { private DebuggerCommands commands = null; private List swfs = new ArrayList<>(); private boolean paused = true; - private Map modulePaths = new HashMap<>(); - private Map classToModule = new HashMap<>(); + private Map modulePaths = new HashMap<>(); + private Map classToModule = new HashMap<>(); + + private Map> toAddBPointMap = new HashMap<>(); + private Map> confirmedPointMap = new HashMap<>(); + private Map> invalidBreakPointMap = new HashMap<>(); + private Map> toRemoveBPointMap = new HashMap<>(); + + private int breakIp = -1; + private String breakScriptName = null; + + public int getBreakIp() { + return breakIp; + } + + public String getBreakScriptName() { + return breakScriptName; + } + + public synchronized void removeBreakPoint(String scriptName, int line) { + if (isBreakpointInvalid(scriptName, line)) { + return; + } + if (isBreakpointToAdd(scriptName, line)) { + toAddBPointMap.get(scriptName).remove(line); + if (toAddBPointMap.get(scriptName).isEmpty()) { + toAddBPointMap.remove(scriptName); + } + } else if (isBreakpointConfirmed(scriptName, line)) { + if (!toRemoveBPointMap.containsKey(scriptName)) { + toRemoveBPointMap.put(scriptName, new TreeSet<>()); + } + toRemoveBPointMap.get(scriptName).add(line); + } + try { + sendBreakPoints(false); + } catch (IOException ex) { + //ignore + } + } + + public synchronized Set getBreakPoints(String scriptName) { + Set lines = new TreeSet<>(); + if (confirmedPointMap.containsKey(scriptName)) { + lines.addAll(confirmedPointMap.get(scriptName)); + } + if (toAddBPointMap.containsKey(scriptName)) { + lines.addAll(toAddBPointMap.get(scriptName)); + } + return lines; + } + + public synchronized void clearBreakPoints() { + for (String scriptName : confirmedPointMap.keySet()) { + if (!toAddBPointMap.containsKey(scriptName)) { + toAddBPointMap.put(scriptName, new TreeSet<>()); + } + toAddBPointMap.get(scriptName).addAll(confirmedPointMap.get(scriptName)); + } + confirmedPointMap.clear(); + invalidBreakPointMap.clear(); + } + + public synchronized Map> getAllBreakPoints(boolean validOnly) { + Map> ret = new HashMap<>(); + for (String scriptName : confirmedPointMap.keySet()) { + Set lines = new TreeSet<>(); + lines.addAll(confirmedPointMap.get(scriptName)); + ret.put(scriptName, lines); + } + for (String scriptName : toAddBPointMap.keySet()) { + if (!ret.containsKey(scriptName)) { + ret.put(scriptName, new TreeSet<>()); + } + ret.get(scriptName).addAll(toAddBPointMap.get(scriptName)); + } + if (!validOnly) { + for (String scriptName : invalidBreakPointMap.keySet()) { + if (!ret.containsKey(scriptName)) { + ret.put(scriptName, new TreeSet<>()); + } + ret.get(scriptName).addAll(invalidBreakPointMap.get(scriptName)); + } + } + return ret; + } + + public boolean addBreakPoint(String scriptName, int line) { + synchronized (this) { + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "adding bp " + scriptName + ":" + line); + if (isBreakpointToRemove(scriptName, line)) { + toRemoveBPointMap.get(scriptName).remove(line); + if (toRemoveBPointMap.get(scriptName).isEmpty()) { + toRemoveBPointMap.remove(scriptName); + } + } + + if (isBreakpointConfirmed(scriptName, line)) { + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "bp " + scriptName + ":" + line + " already confirmed"); + return true; + } + if (isBreakpointInvalid(scriptName, line)) { + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "bp " + scriptName + ":" + line + " already invalid"); + return false; + } + if (!toAddBPointMap.containsKey(scriptName)) { + toAddBPointMap.put(scriptName, new TreeSet<>()); + } + toAddBPointMap.get(scriptName).add(line); + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "bp " + scriptName + ":" + line + " added to todo"); + } + try { + sendBreakPoints(false); + } catch (IOException ex) { + //ignored + } + + return true; + } + + public synchronized boolean isBreakpointConfirmed(String scriptName, int line) { + return confirmedPointMap.containsKey(scriptName) && confirmedPointMap.get(scriptName).contains(line); + } + + public synchronized boolean isBreakpointToAdd(String scriptName, int line) { + return toAddBPointMap.containsKey(scriptName) && toAddBPointMap.get(scriptName).contains(line); + } + + public synchronized boolean isBreakpointToRemove(String scriptName, int line) { + return toRemoveBPointMap.containsKey(scriptName) && toRemoveBPointMap.get(scriptName).contains(line); + } + + public synchronized boolean isBreakpointInvalid(String scriptName, int line) { + return invalidBreakPointMap.containsKey(scriptName) && invalidBreakPointMap.get(scriptName).contains(line); + } + + private synchronized void markBreakPointInvalid(String scriptName, int line) { + if (!invalidBreakPointMap.containsKey(scriptName)) { + invalidBreakPointMap.put(scriptName, new TreeSet<>()); + } + invalidBreakPointMap.get(scriptName).add(line); + } private InFrame frame; private InBreakAtExt breakInfo; private InBreakReason breakReason; - private final List varListeners = new ArrayList<>(); + private final List breakListeners = new ArrayList<>(); private final List traceListeners = new ArrayList<>(); private final List clisteners = new ArrayList<>(); - public void notSuspended() { - frame = null; - breakInfo = null; - breakReason = null; - for (VariableChangedListener l : varListeners) { - l.variablesChanged(); - } - } - public String moduleToString(int file) { if (!modulePaths.containsKey(file)) { return "unknown"; } - return modulePaths.get(file).toString(); + return modulePaths.get(file); } - public InBreakAtExt getBreakInfo() { + public synchronized InBreakAtExt getBreakInfo() { + if (!paused) { + return null; + } return breakInfo; } - public InBreakReason getBreakReason() { + public synchronized InBreakReason getBreakReason() { + if (!paused) { + return null; + } return breakReason; } @@ -113,14 +260,16 @@ public class DebuggerHandler implements DebugConnectionListener { } - public static interface VariableChangedListener { + public static interface BreakListener { - public void variablesChanged(); + public void breakAt(String scriptName, int line); + + public void doContinue(); } - public void addVariableChangedListener(VariableChangedListener l) { - varListeners.add(l); + public void addBreakListener(BreakListener l) { + breakListeners.add(l); } public void addTraceListener(TraceListener l) { @@ -131,8 +280,8 @@ public class DebuggerHandler implements DebugConnectionListener { traceListeners.remove(l); } - public void removeVariableChangedListener(VariableChangedListener l) { - varListeners.remove(l); + public void removeBreakListener(BreakListener l) { + breakListeners.remove(l); } public void addConnectionListener(ConnectionListener l) { @@ -143,19 +292,27 @@ public class DebuggerHandler implements DebugConnectionListener { clisteners.remove(l); } - public InFrame getFrame() { + public synchronized InFrame getFrame() { + if (!paused) { + return null; + } return frame; } - public int moduleIdOf(ScriptPack pack) { - if (classToModule.containsKey(pack.getClassPath())) { - return classToModule.get(pack.getClassPath()); + public synchronized int moduleIdOf(String pack) { + if (classToModule.containsKey(pack)) { + return classToModule.get(pack); } return -1; } - public synchronized boolean isPaused() { - return paused; + public boolean isPaused() { + if (!isConnected()) { + return false; + } + synchronized (this) { + return paused; + } } public List getSwfs() { @@ -174,12 +331,12 @@ public class DebuggerHandler implements DebugConnectionListener { for (ConnectionListener l : clisteners) { l.disconnected(); } - for (VariableChangedListener l : varListeners) { - l.variablesChanged(); - } + /*for (BreakListener l : breakListeners) { + l.breakAt(); + }*/ } - public boolean isConnected() { + public synchronized boolean isConnected() { return connected; } @@ -191,7 +348,7 @@ public class DebuggerHandler implements DebugConnectionListener { } private static void enlog(Class cls) { - Level level = Level.FINEST; + Level level = Level.INFO; Logger mylog = Logger.getLogger(cls.getName()); mylog.setLevel(level); @@ -201,24 +358,95 @@ public class DebuggerHandler implements DebugConnectionListener { } @Override - public void connected(DebuggerConnection con) { + public void failedListen(IOException ex) { + View.execInEventDispatchLater(new Runnable() { - synchronized (DebuggerHandler.this) { - paused = true; + @Override + public void run() { + disconnect(); + Main.stopRun(); + Main.stopWork(); + View.showMessageDialog(Main.getMainFrame().getPanel(), AppStrings.translate("error.debug.listen").replace("%port%", "" + Debugger.DEBUG_PORT)); + Main.getMainFrame().getPanel().updateMenu(); + } + }); + + } + + @Override + public void connected(DebuggerConnection con) { + clearBreakPoints(); + + Main.startWork(AppStrings.translate("work.debugging"), null); + + synchronized (this) { + paused = false; } Main.getMainFrame().getPanel().updateMenu(); //enlog(DebuggerConnection.class); //enlog(DebuggerCommands.class); + enlog(DebuggerHandler.class); try { - //rootLog.getHandlers()[0].setLevel(level); con.getMessage(InVersion.class); } catch (IOException ex) { Logger.getLogger(DebuggerHandler.class.getName()).log(Level.SEVERE, null, ex); } - commands = new DebuggerCommands(con); + + //Respon to InProcessTag with OutProcessedTag + con.addMessageListener(new DebugMessageListener() { + @Override + public void message(InProcessTag message) { + try { + con.writeMessage(new OutProcessedTag(con)); + } catch (IOException ex) { + //disconnect(); + //ignore + } + } + }); + + Map moduleNames = new HashMap<>(); + try { + + int numScript = con.getMessage(InNumScript.class).num; + for (int i = 0; i < numScript; i++) { + InScript sc = con.getMessage(InScript.class); + moduleNames.put(sc.module, sc.name); + } + + modulePaths = new HashMap<>(); + classToModule = new HashMap<>(); + //Pattern patMainFrame = Pattern.compile("^Actions for Scene ([0-9]+): Frame ([0-9]+) of Layer Name .*$"); + //Pattern patSymbol = Pattern.compile("^Actions for Symbol ([0-9]+): Frame ([0-9]+) of Layer Name .*$"); + //Pattern patAS2 = Pattern.compile("^([^:]+): .*\\.as$"); + Pattern patAS3 = Pattern.compile("^(.*);(.*);(.*)\\.as$"); + for (int file : moduleNames.keySet()) { + String name = moduleNames.get(file); + String[] parts = name.split(";"); + + Matcher m; + /*if ((m = patMainFrame.matcher(name)).matches()) { + name = "\\frame_" + m.group(2) + "\\DoAction"; + } else if ((m = patSymbol.matcher(name)).matches()) { + name = "\\DefineSprite(" + m.group(1) + ")\\frame_" + m.group(2) + "\\DoAction"; + } else if ((m = patAS2.matcher(name)).matches()) { + name = "\\_Packages\\" + m.group(1).replace(".", "\\"); + } else*/ + if ((m = patAS3.matcher(name)).matches()) { + String clsName = m.group(3); + String pkg = m.group(2).replace("\\", "."); + name = DottedChain.parse(pkg).add(clsName).toString(); + } + modulePaths.put(file, name); + classToModule.put(name, file); + } + + //con.getMessage(InSetBreakpoint.class); + commands = new DebuggerCommands(con); + commands.stopWarning(); commands.setStopOnFault(); commands.setEnumerateOverride(); @@ -227,43 +455,56 @@ public class DebuggerHandler implements DebugConnectionListener { commands.setSwfLoadNotify(); commands.setGetterTimeout(1500); commands.setSetterTimeout(5000); - commands.squelch(true); - swfs = commands.getSwfInfo(1); - - Map moduleNames = new HashMap<>(); - - modulePaths = new HashMap<>(); - classToModule = new HashMap<>(); - - int numScript = con.getMessage(InNumScript.class).num; - for (int i = 0; i < numScript; i++) { - InScript sc = con.getMessage(InScript.class); - moduleNames.put(sc.module, sc.name); + con.wideLines = commands.getOption("wide_line_player", "false").equals("true"); + if (con.wideLines) { + commands.setOption("wide_line_debugger", "on"); } + commands.squelch(true); - for (int mname : moduleNames.keySet()) { - String name = moduleNames.get(mname); - String[] parts = name.split(";"); + swfs = commands.getSwfInfo(1); + con.sendMessage(new OutGetSwf(con, 0), InGetSwf.class); + InGetSwd iswd = con.sendMessage(new OutGetSwd(con, 0), InGetSwd.class); - if (parts.length == 3) { - String clsName = parts[2].replace(".as", ""); - String pkg = parts[1].replace("/", "\\").replace("\\", "."); - ClassPath cp = new ClassPath(DottedChain.parse(pkg), clsName); - modulePaths.put(mname, cp); - classToModule.put(cp, mname); + boolean isAS3 = (Main.getMainFrame().getPanel().getCurrentSwf().isAS3()); + + InSetBreakpoint isb = con.getMessage(InSetBreakpoint.class); + synchronized (this) { + for (int i = 0; i < isb.files.size(); i++) { + String sname = moduleNames.get(isb.files.get(i)); + if (!confirmedPointMap.containsKey(sname)) { + confirmedPointMap.put(sname, new TreeSet<>()); + } + if (toAddBPointMap.containsKey(sname)) { + toAddBPointMap.get(sname).remove(isb.lines.get(i)); + if (toAddBPointMap.get(sname).isEmpty()) { + toAddBPointMap.remove(sname); + } + } + confirmedPointMap.get(sname).add(isb.lines.get(i)); + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "Breakpoint {0}:{1} submitted successfully", new Object[]{sname, isb.lines.get(i)}); } } - con.getMessage(InSetBreakpoint.class); - con.getMessage(InAskBreakpoints.class); - con.addMessageListener(new DebugMessageListener() { + synchronized (this) { + connected = true; + } + con.addMessageListener(new DebugMessageListener() { + @Override + public void message(InAskBreakpoints message) { + + } + }); + con.addMessageListener(new DebugMessageListener() { @Override public void message(InContinue msg) { synchronized (DebuggerHandler.this) { paused = false; + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINE, "continued"); + } + for (BreakListener bl : breakListeners) { + bl.doContinue(); } - Main.getMainFrame().getPanel().updateMenu(); } }); con.addMessageListener(new DebugMessageListener() { @@ -272,89 +513,122 @@ public class DebuggerHandler implements DebugConnectionListener { public void message(InBreakAt message) { synchronized (DebuggerHandler.this) { paused = true; + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINE, "paused"); } - View.execInEventDispatchLater(new Runnable() { - @Override - public void run() { + String newBreakScriptName = "unknown"; + if (modulePaths.containsKey(message.file)) { + //Logger.getLogger(DebuggerCommands.class.getName()).log(Level.SEVERE, "Invalid file: " + message.file); + newBreakScriptName = modulePaths.get(message.file); + } - Main.getMainFrame().getPanel().updateMenu(); - Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "break at {0}:{1}", new Object[]{moduleNames.get(message.file), message.line}); - if (!modulePaths.containsKey(message.file)) { + try { + breakInfo = con.getMessage(InBreakAtExt.class); + breakReason = con.sendMessage(new OutGetBreakReason(con), InBreakReason.class); + + final String[] reasonNames = new String[]{"unknown", "breakpoint", "watch", "fault", "stopRequest", "step", "halt", "scriptLoaded"}; + String reason = breakReason.reason < reasonNames.length ? reasonNames[breakReason.reason] : reasonNames[0]; + + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINE, "break at {0}:{1}, reason: {2}", new Object[]{newBreakScriptName, message.line, reason}); + + sendBreakPoints(false); + synchronized (DebuggerHandler.this) { + breakScriptName = newBreakScriptName; + breakIp = message.line; + } + + if (breakReason.reason == InBreakReason.REASON_SCRIPT_LOADED) { + if (!Configuration.debugHalt.get()) { + commands.sendContinue(); return; } - - ClassPath cls = modulePaths.get(message.file); - Main.startWork(AppStrings.translate("work.breakat") + cls + ":" + message.line, null); - - try { - breakInfo = con.getMessage(InBreakAtExt.class); - breakReason = con.sendMessage(new OutGetBreakReason(con), InBreakReason.class); - frame = commands.getFrame(0); - - for (VariableChangedListener l : varListeners) { - l.variablesChanged(); - } - - } catch (IOException ex) { - //ignore - } - Main.breakAt(cls, message.line); + Main.startWork(AppStrings.translate("work.halted"), null); + } else { + Main.startWork(AppStrings.translate("work.breakat") + newBreakScriptName + ":" + message.line + " " + AppStrings.translate("debug.break.reason." + reason), null); } + frame = commands.getFrame(0); + + for (BreakListener l : breakListeners) { + l.breakAt(newBreakScriptName, message.line); + } + + } catch (IOException ex) { + //ignore } - ); - //dc.sendContinue(); + } }); - //commands.sendContinue(); - List packs = Main.getMainFrame().getPanel().getCurrentSwf().getAS3Packs(); - for (ScriptPack sp : packs) { - ClassPath cp = sp.getClassPath(); - if (classToModule.containsKey(cp)) { - int file = classToModule.get(cp); - Set bpts = new TreeSet<>(Main.getPackBreakPoints(sp)); - for (int line : bpts) { - if (!commands.addBreakPoint(file, line)) { - Main.markBreakPointInvalid(sp, line); - } - } - } - } - - Main.getMainFrame() - .getPanel().refreshBreakPoints(); - connected = true; for (ConnectionListener l : clisteners) { l.connected(); } - if (Configuration.debugHalt.get()) { - Main.startWork(AppStrings.translate("work.halted"), null); - } else { + con.addMessageListener(new DebugMessageListener() { + + @Override + public void message(InTrace tr) { + for (TraceListener l : traceListeners) { + l.trace(tr.text); + } + } + }); + + if (!isAS3) { + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINER, "End of connect - sending continue"); commands.sendContinue(); } - new CancellableWorker() { - - @Override - protected Object doInBackground() throws Exception { - try { - while (isConnected()) { - InTrace tr = con.getMessage(InTrace.class); - for (TraceListener l : traceListeners) { - l.trace(tr.text); - } - } - } catch (IOException ex) { - //ignore - } - return null; - } - }.execute(); - } catch (IOException ex) { - connected = false; + + synchronized (this) { + connected = false; + } } } + + private void sendBreakPoints(boolean force) throws IOException { + if (!force && !isPaused()) { + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINEST, "not sending bps, not paused"); + return; + } + synchronized (this) { + for (String scriptName : toRemoveBPointMap.keySet()) { + int file = moduleIdOf(scriptName); + if (file > -1) { + for (int line : toRemoveBPointMap.get(scriptName)) { + if (isBreakpointConfirmed(scriptName, line)) { + commands.removeBreakPoint(file, line); + confirmedPointMap.get(scriptName).remove(line); + if (confirmedPointMap.get(scriptName).isEmpty()) { + confirmedPointMap.remove(scriptName); + } + } + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "Breakpoint {0}:{1} removed", new Object[]{scriptName, line}); + } + } + } + toRemoveBPointMap.clear(); + + for (String scriptName : toAddBPointMap.keySet()) { + int file = moduleIdOf(scriptName); + if (file > -1) { + for (int line : toAddBPointMap.get(scriptName)) { + if (commands.addBreakPoint(file, line)) { + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "Breakpoint {0}:{1} submitted successfully", new Object[]{scriptName, line}); + if (!confirmedPointMap.containsKey(scriptName)) { + confirmedPointMap.put(scriptName, new TreeSet<>()); + } + confirmedPointMap.get(scriptName).add(line); + } else { + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "Breakpoint {0}:{1} unable to submit", new Object[]{scriptName, line}); + markBreakPointInvalid(scriptName, line); + } + } + } + } + toAddBPointMap.clear(); + } + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINEST, "sending bps finished"); + + } } diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index c8ecd5f1f..cf3abc96d 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -21,6 +21,7 @@ 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.SWD; import com.jpexs.debugger.flash.messages.in.InAskBreakpoints; import com.jpexs.debugger.flash.messages.in.InBreakAt; import com.jpexs.debugger.flash.messages.in.InNumScript; @@ -81,6 +82,7 @@ import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.net.Proxy; @@ -159,117 +161,285 @@ public class Main { private static DebuggerHandler debugHandler = null; - private static Map> breakPointMap = new WeakHashMap<>(); - private static Map> invalidBreakPointMap = new WeakHashMap<>(); - private static int ip = 0; - private static ClassPath ipClass = null; + //private static int ip = 0; + //private static String ipClass = null; + private static Process runProcess; + private static boolean runProcessDebug; - public static void debuggerNotSuspended() { - getDebugHandler().notSuspended(); + private static boolean inited = false; + + private static File runTempFile; + + public static void freeRun() { + synchronized (Main.class) { + if (runTempFile != null) { + runTempFile.delete(); + runTempFile = null; + } + + runProcess = null; + } mainFrame.getPanel().clearDebuggerColors(); - ip = 0; - ipClass = null; - } - - public static void clearBreakPoints(ScriptPack pack) { - if (breakPointMap.containsKey(pack)) { - breakPointMap.remove(pack); + if (runProcessDebug) { + Main.getDebugHandler().disconnect(); } } - public static boolean isDebugging() { - return mainFrame.getMenu().isDebugRunning(); + public static synchronized boolean isDebugPaused() { + return runProcess != null && runProcessDebug && getDebugHandler().isPaused(); } - public static int getIp(ScriptPack pack) { - return ip; + public static synchronized boolean isDebugRunning() { + return runProcess != null && runProcessDebug; } - public static ClassPath getIpClass() { - return ipClass; + public static synchronized boolean isDebugConnected() { + return getDebugHandler().isConnected(); } - public static void breakAt(ClassPath clsName, int ip) { - Main.ip = ip; - Main.ipClass = clsName; - mainFrame.getPanel().gotoClassLine(getMainFrame().getPanel().getCurrentSwf(), clsName.toString(), ip); - //Main.getMainFrame().getPanel().debuggerBreakAt(Main.getMainFrame().getPanel().getCurrentSwf(), cls, message.line); + public static synchronized boolean isRunning() { + return runProcess != null && !runProcessDebug; } - public static boolean isBreakPointValid(ScriptPack pack, int line) { - if (!invalidBreakPointMap.containsKey(pack)) { - return true; + public static void runPlayer(String title, final String exePath, String file, String flashVars) { + if (flashVars != null && !flashVars.isEmpty()) { + file += "?" + flashVars; } - return !invalidBreakPointMap.get(pack).contains(line); - - } - - public static void markBreakPointInvalid(ScriptPack pack, int line) { - if (!invalidBreakPointMap.containsKey(pack)) { - invalidBreakPointMap.put(pack, new TreeSet<>()); + if (!new File(file).exists()) { + return; } - invalidBreakPointMap.get(pack).add(line); - } - public static void addBreakPoint(ScriptPack pack, int line) { - if (!breakPointMap.containsKey(pack)) { - breakPointMap.put(pack, new TreeSet<>()); - } - breakPointMap.get(pack).add(line); - if (debugHandler.isConnected()) { - int file = debugHandler.moduleIdOf(pack); - if (file > -1) { + final String ffile = file; + + CancellableWorker runWorker = new CancellableWorker() { + + @Override + protected Object doInBackground() throws Exception { + Process proc; try { - if (!debugHandler.getCommands().addBreakPoint(file, line)) { - markBreakPointInvalid(pack, line); + proc = Runtime.getRuntime().exec("\"" + exePath + "\" \"file://" + ffile + "\""); + } catch (IOException ex) { + Logger.getLogger(MainFrameMenu.class.getName()).log(Level.SEVERE, null, ex); + + return null; + } + boolean isDebug; + + synchronized (Main.class) { + runProcess = proc; + isDebug = runProcessDebug; + } + if (isDebug) { + mainFrame.getMenu().hilightPath("/debugging"); + } + mainFrame.getMenu().updateComponents(); + try { + if (proc != null) { + proc.waitFor(); + } + } catch (InterruptedException ex) { + if (proc != null) { + try { + proc.destroy(); + } catch (Exception ex2) { + //ignore + } } - } catch (IOException ex) { - debugHandler.disconnect(); - //ignore } + freeRun(); + mainFrame.getMenu().updateComponents(); + return null; } + + @Override + protected void done() { + Main.stopWork(); + } + + @Override + public void workerCancelled() { + Main.stopWork(); + synchronized (Main.class) { + if (runProcess != null) { + try { + runProcess.destroy(); + } catch (Exception ex) { + + } + } + } + freeRun(); + mainFrame.getMenu().updateComponents(); + } + + }; + + mainFrame.getMenu().updateComponents(); + Main.startWork(title + "...", runWorker); + runWorker.execute(); + } + + public static void stopRun() { + + synchronized (Main.class) { + if (runProcess != null) { + runProcess.destroy(); + } + } + freeRun(); + stopDebugger(); + mainFrame.getMenu().updateComponents(); + } + + public static void run(SWF swf) { + String flashVars = "";//key=val&key2=val2 + String playerLocation = Configuration.playerLocation.get(); + if (playerLocation.isEmpty() || (!new File(playerLocation).exists())) { + View.showMessageDialog(null, AppStrings.translate("message.playerpath.notset"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + advancedSettings("paths"); + return; + } + if (swf == null) { + return; + } + File tempFile; + try { + tempFile = File.createTempFile("ffdec_run_", ".swf"); + + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { + swf.saveTo(fos); + } + } catch (IOException ex) { + return; + + } + if (tempFile != null) { + synchronized (Main.class) { + runTempFile = tempFile; + runProcessDebug = false; + } + runPlayer(AppStrings.translate("work.running"), playerLocation, tempFile.getAbsolutePath(), flashVars); } } - public static void removeBreakPoint(ScriptPack pack, int line) { - if (breakPointMap.containsKey(pack)) { - Set lines = breakPointMap.get(pack); - if (lines != null) { - lines.remove(line); - } + public static void runDebug(SWF swf) { + String flashVars = "";//key=val&key2=val2 + String playerLocation = Configuration.playerDebugLocation.get(); + if (playerLocation.isEmpty() || (!new File(playerLocation).exists())) { + View.showMessageDialog(null, AppStrings.translate("message.playerpath.debug.notset"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + Main.advancedSettings("paths"); + return; } - if (debugHandler.isConnected()) { - int file = debugHandler.moduleIdOf(pack); - if (file > -1) { - try { - debugHandler.getCommands().removeBreakPoint(file, line); - } catch (IOException ex) { - debugHandler.disconnect(); - //ignore + if (swf == null) { + return; + } + File tempFile = null; + + try { + tempFile = File.createTempFile("ffdec_debug_", ".swf"); + } catch (Exception ex) { + + } + if (tempFile != null) { + final File fTempFile = tempFile; + CancellableWorker instrumentWorker = new CancellableWorker() { + + @Override + protected Object doInBackground() throws Exception { + + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(fTempFile))) { + swf.saveTo(fos); + } + //Inject Loader + SWF instrSWF = null; + try (FileInputStream fis = new FileInputStream(fTempFile)) { + instrSWF = new SWF(fis, false, false); + + } catch (InterruptedException ex) { + Logger.getLogger(MainFrameMenu.class + .getName()).log(Level.SEVERE, null, ex); + } + if (instrSWF != null) { + if (instrSWF.isAS3()) { + instrSWF.enableDebugging(true, new File(".")); + } else { + instrSWF.enableDebugging(false, new File(".")); + File swdFile = new File(fTempFile.getAbsolutePath().replace(".swf", ".swd")); + instrSWF.generateSwdFile(swdFile, getPackBreakPoints(true)); + } + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(fTempFile))) { + instrSWF.saveTo(fos); + } + } + return null; } - } + + @Override + public void workerCancelled() { + Main.stopWork(); + } + + @Override + protected void done() { + synchronized (Main.class) { + runTempFile = fTempFile; + runProcessDebug = true; + } + Main.stopWork(); + Main.startDebugger(); + runPlayer(AppStrings.translate("work.debugging.wait"), playerLocation, fTempFile.getAbsolutePath(), flashVars); + } + + }; + + Main.startWork(AppStrings.translate("work.debugging.instrumenting"), instrumentWorker); + instrumentWorker.execute(); } } - public static boolean toggleBreakPoint(ScriptPack pack, int line) { - if (!breakPointMap.containsKey(pack)) { - addBreakPoint(pack, line); - return true; - } - if (breakPointMap.get(pack).contains(line)) { - removeBreakPoint(pack, line); + /* public static void debuggerNotSuspended() { + + }*/ + public static boolean isDebugging() { + return isDebugRunning(); + } + + public synchronized static int getIp(Object pack) { + return getDebugHandler().getBreakIp(); + } + + public synchronized static String getIpClass() { + return getDebugHandler().getBreakScriptName(); + } + + public static synchronized boolean isBreakPointValid(String scriptName, int line) { + return !getDebugHandler().isBreakpointInvalid(scriptName, line); + } + + public synchronized static void addBreakPoint(String scriptName, int line) { + getDebugHandler().addBreakPoint(scriptName, line); + } + + public synchronized static void removeBreakPoint(String scriptName, int line) { + getDebugHandler().removeBreakPoint(scriptName, line); + } + + public synchronized static boolean toggleBreakPoint(String scriptName, int line) { + if (getDebugHandler().isBreakpointToAdd(scriptName, line) || getDebugHandler().isBreakpointConfirmed(scriptName, line)) { + getDebugHandler().removeBreakPoint(scriptName, line); return false; } else { - addBreakPoint(pack, line); + getDebugHandler().addBreakPoint(scriptName, line); return true; } } - public static Set getPackBreakPoints(ScriptPack pack) { - if (!breakPointMap.containsKey(pack)) { - return new HashSet<>(); - } - return breakPointMap.get(pack); + public synchronized static Map> getPackBreakPoints(boolean validOnly) { + return getDebugHandler().getAllBreakPoints(validOnly); + } + + public synchronized static Set getScriptBreakPoints(String pack) { + return getDebugHandler().getBreakPoints(pack); } public static DebuggerHandler getDebugHandler() { @@ -319,6 +489,14 @@ public class Main { } } + public synchronized static boolean isInited() { + return inited; + } + + public synchronized static void setSessionLoaded(boolean v) { + inited = v; + } + public static boolean isWorking() { return working; } @@ -1151,26 +1329,51 @@ public class Main { */ flashDebugger = new Debugger(); debugHandler = new DebuggerHandler(); + debugHandler.addBreakListener(new DebuggerHandler.BreakListener() { + + @Override + public void doContinue() { + mainFrame.getPanel().clearDebuggerColors(); + } + + @Override + public void breakAt(String scriptName, int line) { + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + mainFrame.getPanel().gotoClassLine(getMainFrame().getPanel().getCurrentSwf(), scriptName, line); + } + }); + } + }); debugHandler.addConnectionListener(new DebuggerHandler.ConnectionListener() { @Override public void connected() { + Main.mainFrame.getMenu().updateComponents(); } @Override public void disconnected() { - ip = 0; - ipClass = null; + } }); flashDebugger.addConnectionListener(debugHandler); - flashDebugger.start(); } catch (IOException ex) { - //ignore + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "eeex", ex); } } + public static void startDebugger() { + flashDebugger.startDebugger(); + } + + public static void stopDebugger() { + flashDebugger.stopDebugger(); + } + public static void showModeFrame() { ensureMainFrame(); mainFrame.setVisible(true); @@ -1371,6 +1574,7 @@ public class Main { * @throws IOException On error */ public static void main(String[] args) throws IOException { + setSessionLoaded(false); clearTemp(); try { @@ -1400,6 +1604,7 @@ public class Main { } }); } else { + setSessionLoaded(true); String[] filesToOpen = CommandLineArgumentParser.parseArguments(args); if (filesToOpen != null && filesToOpen.length > 0) { View.execInEventDispatch(() -> { @@ -1447,9 +1652,16 @@ public class Main { if (sourceInfos.length > 0) { openFile(sourceInfos, () -> { mainFrame.getPanel().tagTree.setSelectionPathString(Configuration.lastSessionSelection.get()); + setSessionLoaded(true); }); + } else { + setSessionLoaded(true); } + } else { + setSessionLoaded(true); } + } else { + setSessionLoaded(true); } } diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java index 256b29150..f37704a6b 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.debugger.flash.DebuggerCommands; +import com.jpexs.debugger.flash.messages.out.OutStepContinue; import com.jpexs.decompiler.flash.ApplicationInfo; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFBundle; @@ -661,10 +662,11 @@ public abstract class MainFrameMenu implements MenuBuilder { public void updateComponents(SWF swf) { this.swf = swf; - boolean isRunning = isRunning(); - boolean isDebugRunning = isDebugRunning(); + boolean isRunning = Main.isRunning(); + boolean isDebugRunning = Main.isDebugRunning(); + boolean isDebugPaused = Main.isDebugPaused(); + boolean isRunningOrDebugging = isRunning || isDebugRunning; - boolean isDebugPaused = isDebugPaused(); boolean swfSelected = swf != null; boolean isWorking = Main.isWorking(); @@ -725,7 +727,7 @@ public abstract class MainFrameMenu implements MenuBuilder { setMenuEnabled("/help/about", !isWorking); setMenuEnabled("/file/start/run", swfSelected && !isRunningOrDebugging); - setMenuEnabled("/file/start/debug", hasAbc && !isRunningOrDebugging); + setMenuEnabled("/file/start/debug", !isRunningOrDebugging); setMenuEnabled("/file/start/stop", isRunningOrDebugging); setMenuEnabled("/debugging/debug/stop", isRunningOrDebugging); //same as previous @@ -1098,231 +1100,22 @@ public abstract class MainFrameMenu implements MenuBuilder { KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); manager.removeKeyEventDispatcher(keyEventDispatcher); - if (runProcess != null) { - try { - runProcess.destroy(); - } catch (Exception ex) { - - } - } - } - - private Process runProcess; - private boolean runProcessDebug; - - private File runTempFile; - - public synchronized boolean isDebugPaused() { - return runProcess != null && runProcessDebug && Main.getDebugHandler().isPaused(); - } - - public synchronized boolean isDebugRunning() { - return runProcess != null && runProcessDebug; - } - - public synchronized boolean isRunning() { - return runProcess != null && !runProcessDebug; - } - - private synchronized void freeRun() { - if (runTempFile != null) { - runTempFile.delete(); - runTempFile = null; - } - runProcess = null; - mainFrame.getPanel().clearDebuggerColors(); - if (runProcessDebug) { - Main.getDebugHandler().disconnect(); - } - } - - private void runPlayer(String title, final String exePath, String file, String flashVars) { - if (flashVars != null && !flashVars.isEmpty()) { - file += "?" + flashVars; - } - if (!new File(file).exists()) { - return; - } - - final String ffile = file; - - CancellableWorker runWorker = new CancellableWorker() { - - @Override - protected Object doInBackground() throws Exception { - Process proc; - try { - proc = Runtime.getRuntime().exec("\"" + exePath + "\" \"file://" + ffile + "\""); - } catch (IOException ex) { - Logger.getLogger(MainFrameMenu.class.getName()).log(Level.SEVERE, null, ex); - - return null; - } - synchronized (this) { - runProcess = proc; - } - if (runProcessDebug) { - hilightPath("/debugging"); - } - updateComponents(); - try { - if (proc != null) { - proc.waitFor(); - } - } catch (InterruptedException ex) { - if (proc != null) { - try { - proc.destroy(); - } catch (Exception ex2) { - //ignore - } - } - } - freeRun(); - updateComponents(); - return null; - } - - @Override - protected void done() { - Main.stopWork(); - } - - @Override - public void workerCancelled() { - Main.stopWork(); - synchronized (MainFrameMenu.this) { - if (runProcess != null) { - try { - runProcess.destroy(); - } catch (Exception ex) { - - } - } - } - freeRun(); - updateComponents(); - } - - }; - - updateComponents(); - Main.startWork(title + "...", runWorker); - runWorker.execute(); + Main.stopRun(); } public boolean runActionPerformed(ActionEvent evt) { - String flashVars = "";//key=val&key2=val2 - String playerLocation = Configuration.playerLocation.get(); - if (playerLocation.isEmpty() || (!new File(playerLocation).exists())) { - View.showMessageDialog(null, AppStrings.translate("message.playerpath.notset"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - Main.advancedSettings("paths"); - return true; - } - if (swf == null) { - return true; - } - File tempFile; - try { - tempFile = File.createTempFile("ffdec_run_", ".swf"); - - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { - swf.saveTo(fos); - } - } catch (IOException ex) { - return true; - - } - if (tempFile != null) { - synchronized (this) { - runTempFile = tempFile; - runProcessDebug = false; - } - runPlayer(AppStrings.translate("work.running"), playerLocation, tempFile.getAbsolutePath(), flashVars); - } + Main.run(swf); return true; } public boolean debugActionPerformed(ActionEvent evt) { - String flashVars = "";//key=val&key2=val2 - String playerLocation = Configuration.playerDebugLocation.get(); - if (playerLocation.isEmpty() || (!new File(playerLocation).exists())) { - View.showMessageDialog(null, AppStrings.translate("message.playerpath.debug.notset"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - Main.advancedSettings("paths"); - return true; - } - if (swf == null) { - return true; - } - File tempFile = null; - - try { - tempFile = File.createTempFile("ffdec_debug_", ".swf"); - } catch (Exception ex) { - - } - if (tempFile != null) { - final File fTempFile = tempFile; - CancellableWorker instrumentWorker = new CancellableWorker() { - - @Override - protected Object doInBackground() throws Exception { - - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(fTempFile))) { - swf.saveTo(fos); - } - //Inject Loader - SWF instrSWF = null; - try (FileInputStream fis = new FileInputStream(fTempFile)) { - instrSWF = new SWF(fis, false, false); - - } catch (InterruptedException ex) { - Logger.getLogger(MainFrameMenu.class - .getName()).log(Level.SEVERE, null, ex); - } - if (instrSWF != null) { - instrSWF.enableDebugging(true, new File(".")); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(fTempFile))) { - instrSWF.saveTo(fos); - } - } - return null; - } - - @Override - public void workerCancelled() { - Main.stopWork(); - } - - @Override - protected void done() { - synchronized (MainFrameMenu.this) { - runTempFile = fTempFile; - runProcessDebug = true; - } - Main.stopWork(); - runPlayer(AppStrings.translate("work.debugging"), playerLocation, fTempFile.getAbsolutePath(), flashVars); - } - - }; - - Main.startWork(AppStrings.translate("work.debugging.instrumenting"), instrumentWorker); - instrumentWorker.execute(); - } + Main.runDebug(swf); return true; } public boolean stopActionPerformed(ActionEvent evt) { - - synchronized (this) { - if (runProcess != null) { - runProcess.destroy(); - } - } - freeRun(); - - updateComponents(); + Main.stopRun(); return true; } @@ -1339,7 +1132,6 @@ public abstract class MainFrameMenu implements MenuBuilder { } public boolean stepOverActionPerformed(ActionEvent evt) { - Main.debuggerNotSuspended(); try { @@ -1356,8 +1148,6 @@ public abstract class MainFrameMenu implements MenuBuilder { } public boolean stepIntoActionPerformed(ActionEvent evt) { - Main.debuggerNotSuspended(); - try { DebuggerCommands cmd = Main.getDebugHandler().getCommands(); mainFrame.getPanel().clearDebuggerColors(); @@ -1373,8 +1163,6 @@ public abstract class MainFrameMenu implements MenuBuilder { } public boolean stepOutActionPerformed(ActionEvent evt) { - Main.debuggerNotSuspended(); - try { DebuggerCommands cmd = Main.getDebugHandler().getCommands(); mainFrame.getPanel().clearDebuggerColors(); @@ -1389,10 +1177,9 @@ public abstract class MainFrameMenu implements MenuBuilder { } public boolean continueActionPerformed(ActionEvent evt) { - Main.debuggerNotSuspended(); - try { DebuggerCommands cmd = Main.getDebugHandler().getCommands(); + mainFrame.getPanel().clearDebuggerColors(); Main.startWork(AppStrings.translate("work.debugging") + "...", null); cmd.sendContinue(); } catch (IOException ex) { diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java b/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java index f75089beb..cd6c33b07 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java @@ -539,18 +539,22 @@ public class MainFrameRibbonMenu extends MainFrameMenu { throw new IllegalArgumentException("Menu not found: " + path); } Object o = menuItems.get(path); - if (o instanceof JRibbonBand) { - ((JRibbonBand) o).setEnabled(enabled); - } else if (o instanceof AbstractCommandButton) { - ((AbstractCommandButton) o).setEnabled(enabled); - } else if (o instanceof RibbonApplicationMenuEntryPrimary) { - ((RibbonApplicationMenuEntryPrimary) o).setEnabled(enabled); - } else if (o instanceof RibbonApplicationMenuEntryFooter) { - ((RibbonApplicationMenuEntryFooter) o).setEnabled(enabled); - } else if (o instanceof JComponent) { - ((JComponent) o).setEnabled(enabled); - } else { - throw new IllegalArgumentException("Cannot set enabled to: " + path); + try { + if (o instanceof JRibbonBand) { + ((JRibbonBand) o).setEnabled(enabled); + } else if (o instanceof AbstractCommandButton) { + ((AbstractCommandButton) o).setEnabled(enabled); + } else if (o instanceof RibbonApplicationMenuEntryPrimary) { + ((RibbonApplicationMenuEntryPrimary) o).setEnabled(enabled); + } else if (o instanceof RibbonApplicationMenuEntryFooter) { + ((RibbonApplicationMenuEntryFooter) o).setEnabled(enabled); + } else if (o instanceof JComponent) { + ((JComponent) o).setEnabled(enabled); + } else { + throw new IllegalArgumentException("Cannot set enabled to: " + path); + } + } catch (Exception ex) { + //some substance issues, ignore } } @@ -594,7 +598,11 @@ public class MainFrameRibbonMenu extends MainFrameMenu { @Override public void run() { - ribbon.setVisible(rg, val); + try { + ribbon.setVisible(rg, val); + } catch (Exception ex) { + + } } }); diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 30e09eb27..b105367b4 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -142,6 +142,7 @@ import com.jpexs.decompiler.flash.types.MATRIX; import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.sound.SoundFormat; import com.jpexs.decompiler.flash.xfl.FLAVersion; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; import com.jpexs.helpers.Path; @@ -320,6 +321,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se private static final Logger logger = Logger.getLogger(MainPanel.class.getName()); + private Map asms = new HashMap<>(); + public void setPercent(int percent) { progressBar.setValue(percent); progressBar.setVisible(true); @@ -816,6 +819,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se mainFrame.setTitle(ApplicationInfo.applicationVerName + (Configuration.displayFileName.get() ? " - " + swf.getFileTitle() : "")); List abcList = swf.getAbcList(); + asms = swf.getASMs(true); boolean hasAbc = !abcList.isEmpty(); @@ -1533,9 +1537,11 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } public void gotoClassLine(SWF swf, String cls, int line) { - gotoClass(swf, cls); + gotoScriptName(swf, cls); if (abcPanel != null) { abcPanel.decompiledTextArea.gotoLine(line); + } else if (actionPanel != null) { + actionPanel.decompiledEditor.gotoLine(line); } refreshBreakPoints(); @@ -1561,15 +1567,21 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se }*/ - public void gotoClass(SWF swf, String cls) { + public void gotoScriptName(SWF swf, String scriptName) { if (swf == null) { return; } - List abcList = swf.getAbcList(); - if (!abcList.isEmpty()) { - ABCPanel abcPanel = getABCPanel(); - abcPanel.setAbc(abcList.get(0).getABC()); - abcPanel.hilightScript(swf, cls); + if (swf.isAS3()) { + List abcList = swf.getAbcList(); + if (!abcList.isEmpty()) { + ABCPanel abcPanel = getABCPanel(); + abcPanel.setAbc(abcList.get(0).getABC()); + abcPanel.hilightScript(swf, scriptName); + } + } else { + if (actionPanel != null && asms.containsKey(scriptName)) { + actionPanel.setSource(asms.get(scriptName), true); + } } } @@ -1583,8 +1595,13 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se List abcList = swf.getAbcList(); if (!abcList.isEmpty()) { ABCPanel abcPanel = getABCPanel(); - abcPanel.setAbc(abcList.get(0).getABC()); - abcPanel.hilightScript(swf, documentClass); + for (ABCContainerTag c : abcList) { + if (c.getABC().findClassByName(documentClass) > -1) { + abcPanel.setAbc(c.getABC()); + abcPanel.hilightScript(swf, documentClass); + break; + } + } } } } @@ -2823,6 +2840,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (abcPanel != null) { abcPanel.decompiledTextArea.removeColorMarkerOnAllLines(DecompiledEditorPane.FG_IP_COLOR, DecompiledEditorPane.BG_IP_COLOR, DecompiledEditorPane.PRIORITY_IP); } + if (actionPanel != null) { + actionPanel.decompiledEditor.removeColorMarkerOnAllLines(DecompiledEditorPane.FG_IP_COLOR, DecompiledEditorPane.BG_IP_COLOR, DecompiledEditorPane.PRIORITY_IP); + } } private void stopFlashPlayer() { @@ -3050,7 +3070,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se setSourceWorker.cancel(true); setSourceWorker = null; } - if (!Main.isWorking() || Main.isDebugging()) { + if (!Main.isInited() || !Main.isWorking() || Main.isDebugging()) { CancellableWorker worker = new CancellableWorker() { @Override diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java index ce3f530a9..dabb36861 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java @@ -16,6 +16,7 @@ */ package com.jpexs.decompiler.flash.gui.abc; +import com.jpexs.decompiler.flash.gui.DebugPanel; import com.jpexs.debugger.flash.Variable; import com.jpexs.debugger.flash.messages.in.InBreakAtExt; import com.jpexs.debugger.flash.messages.in.InBreakReason; diff --git a/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java b/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java index 286711dd1..4cbba59c0 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java @@ -38,6 +38,7 @@ import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; import com.jpexs.decompiler.flash.gui.AppStrings; import com.jpexs.decompiler.flash.gui.Main; import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.editor.DebuggableEditorPane; import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightData; @@ -59,7 +60,7 @@ import jsyntaxpane.Token; import jsyntaxpane.TokenType; import jsyntaxpane.components.BreakPointListener; -public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretListener, BreakPointListener { +public class DecompiledEditorPane extends DebuggableEditorPane implements CaretListener { private List highlights = new ArrayList<>(); @@ -91,34 +92,6 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL private final List scriptListeners = new ArrayList<>(); - public static final Color BG_BREAKPOINT_COLOR = new Color(0xfc, 0x9d, 0x9f); - public static final Color FG_BREAKPOINT_COLOR = null; - public static final int PRIORITY_BREAKPOINT = 20; - - public static final Color BG_IP_COLOR = new Color(0xbd, 0xe6, 0xaa); - public static final Color FG_IP_COLOR = null; - public static final int PRIORITY_IP = 0; - - public static final Color BG_INVALID_BREAKPOINT_COLOR = new Color(0xdc, 0xdc, 0xd8); - public static final Color FG_INVALID_BREAKPOINT_COLOR = null; - public static final int PRIORITY_INVALID_BREAKPOINT = 10; - - @Override - public void toggled(int line) { - boolean on = Main.toggleBreakPoint(script, line); - removeColorMarker(line, FG_INVALID_BREAKPOINT_COLOR, BG_INVALID_BREAKPOINT_COLOR, PRIORITY_INVALID_BREAKPOINT); - if (on) { - if (Main.isBreakPointValid(script, line)) { - addColorMarker(line, FG_BREAKPOINT_COLOR, BG_BREAKPOINT_COLOR, PRIORITY_BREAKPOINT); - } else { - addColorMarker(line, FG_INVALID_BREAKPOINT_COLOR, BG_INVALID_BREAKPOINT_COLOR, PRIORITY_INVALID_BREAKPOINT); - } - } else { - removeColorMarker(line, FG_BREAKPOINT_COLOR, BG_BREAKPOINT_COLOR, PRIORITY_BREAKPOINT); - removeColorMarker(line, FG_INVALID_BREAKPOINT_COLOR, BG_INVALID_BREAKPOINT_COLOR, PRIORITY_INVALID_BREAKPOINT); - } - } - public void addScriptListener(Runnable l) { scriptListeners.add(l); } @@ -676,14 +649,16 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL if (!force && this.script == scriptLeaf) { return; } - abcPanel.scriptNameLabel.setText(scriptLeaf.getClassPath().toString()); + String sn = scriptLeaf.getClassPath().toString(); + setScriptName(sn); + abcPanel.scriptNameLabel.setText(sn); int scriptIndex = scriptLeaf.scriptIndex; - ScriptInfo script = null; + ScriptInfo nscript = null; ABC abc = scriptLeaf.abc; if (scriptIndex > -1) { - script = abc.script_info.get(scriptIndex); + nscript = abc.script_info.get(scriptIndex); } - if (script == null) { + if (nscript == null) { highlights = new ArrayList<>(); specialHighlights = new ArrayList<>(); traitHighlights = new ArrayList<>(); @@ -738,36 +713,9 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL return script == null ? null : script.abc; } - public void refreshMarkers() { - removeColorMarkerOnAllLines(FG_BREAKPOINT_COLOR, BG_BREAKPOINT_COLOR, PRIORITY_BREAKPOINT); - removeColorMarkerOnAllLines(FG_INVALID_BREAKPOINT_COLOR, BG_INVALID_BREAKPOINT_COLOR, PRIORITY_INVALID_BREAKPOINT); - removeColorMarkerOnAllLines(FG_IP_COLOR, BG_IP_COLOR, PRIORITY_IP); - - if (script == null) { - return; - } - - Set bkptLines = Main.getPackBreakPoints(script); - - for (int line : bkptLines) { - if (Main.isBreakPointValid(script, line)) { - addColorMarker(line, FG_BREAKPOINT_COLOR, BG_BREAKPOINT_COLOR, PRIORITY_BREAKPOINT); - } else { - addColorMarker(line, FG_INVALID_BREAKPOINT_COLOR, BG_INVALID_BREAKPOINT_COLOR, PRIORITY_INVALID_BREAKPOINT); - } - } - int ip = Main.getIp(script); - ClassPath ipPath = Main.getIpClass(); - if (ip > 0 && ipPath != null && ipPath.equals(script.getClassPath())) { - addColorMarker(ip, FG_IP_COLOR, BG_IP_COLOR, PRIORITY_IP); - } - } - @Override public void setText(String t) { super.setText(t); setCaretPosition(0); - refreshMarkers(); - } } diff --git a/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java b/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java index f1671e7e4..ba2a1b78d 100644 --- a/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.gui.action; import com.jpexs.decompiler.flash.DisassemblyListener; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.ClassPath; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.ActionGraph; import com.jpexs.decompiler.flash.action.ActionList; @@ -34,6 +35,7 @@ import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.flash.gui.AppStrings; +import com.jpexs.decompiler.flash.gui.DebuggerHandler; import com.jpexs.decompiler.flash.gui.GraphDialog; import com.jpexs.decompiler.flash.gui.HeaderLabel; import com.jpexs.decompiler.flash.gui.Main; @@ -42,8 +44,19 @@ import com.jpexs.decompiler.flash.gui.SearchListener; import com.jpexs.decompiler.flash.gui.SearchPanel; import com.jpexs.decompiler.flash.gui.TagEditorPanel; import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.DebugPanel; +import static com.jpexs.decompiler.flash.gui.abc.DecompiledEditorPane.BG_BREAKPOINT_COLOR; +import static com.jpexs.decompiler.flash.gui.abc.DecompiledEditorPane.BG_INVALID_BREAKPOINT_COLOR; +import static com.jpexs.decompiler.flash.gui.abc.DecompiledEditorPane.BG_IP_COLOR; +import static com.jpexs.decompiler.flash.gui.abc.DecompiledEditorPane.FG_BREAKPOINT_COLOR; +import static com.jpexs.decompiler.flash.gui.abc.DecompiledEditorPane.FG_INVALID_BREAKPOINT_COLOR; +import static com.jpexs.decompiler.flash.gui.abc.DecompiledEditorPane.FG_IP_COLOR; +import static com.jpexs.decompiler.flash.gui.abc.DecompiledEditorPane.PRIORITY_BREAKPOINT; +import static com.jpexs.decompiler.flash.gui.abc.DecompiledEditorPane.PRIORITY_INVALID_BREAKPOINT; +import static com.jpexs.decompiler.flash.gui.abc.DecompiledEditorPane.PRIORITY_IP; import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; import com.jpexs.decompiler.flash.gui.controls.NoneSelectedButtonGroup; +import com.jpexs.decompiler.flash.gui.editor.DebuggableEditorPane; import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; import com.jpexs.decompiler.flash.helpers.HighlightedText; @@ -66,6 +79,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.CancellationException; import java.util.logging.Level; import java.util.logging.Logger; @@ -96,7 +110,7 @@ public class ActionPanel extends JPanel implements SearchListener bkptLines = Main.getScriptBreakPoints(sc); + + for (int line : bkptLines) { + if (Main.isBreakPointValid(lastASM, line)) { + decompiledEditor.addColorMarker(line, FG_BREAKPOINT_COLOR, BG_BREAKPOINT_COLOR, PRIORITY_BREAKPOINT); + } else { + decompiledEditor.addColorMarker(line, FG_INVALID_BREAKPOINT_COLOR, BG_INVALID_BREAKPOINT_COLOR, PRIORITY_INVALID_BREAKPOINT); + } + } + int ip = Main.getIp(lastASM); + String ipPath = Main.getIpClass(); + if (ip > 0 && ipPath != null && lastASM.getSwf().getASMs(false).get(ipPath) == lastASM) { + decompiledEditor.addColorMarker(ip, FG_IP_COLOR, BG_IP_COLOR, PRIORITY_IP); + } + }*/ public String getStringUnderCursor() { int pos = decompiledEditor.getCaretPosition(); @@ -206,7 +240,7 @@ public class ActionPanel extends JPanel implements SearchListener h.getProperties().offset) { + if (ins.getOffset() > h.getProperties().offset && lastIns != null) { inspos = (int) (h.getProperties().offset - lastIns.getAddress()); selIns = lastIns; break; @@ -274,9 +308,10 @@ public class ActionPanel extends JPanel implements SearchListener { ignoreCarret = true; + decompiledEditor.setScriptName(scriptName); decompiledEditor.setText(text); ignoreCarret = false; }); @@ -415,12 +450,13 @@ public class ActionPanel extends JPanel implements SearchListener { setSourceWorker = null; - Main.stopWork(); + if (!Main.isDebugging()) { + Main.stopWork(); + } try { get(); } catch (CancellationException ex) { setEditorText("; " + AppStrings.translate("work.canceled"), "text/flasm"); } catch (Exception ex) { - setDecompiledText("// " + AppStrings.translate("decompilationError") + ": " + ex); + setDecompiledText("-", "// " + AppStrings.translate("decompilationError") + ": " + ex); } }); } }; worker.execute(); setSourceWorker = worker; - Main.startWork(AppStrings.translate("work.decompiling") + "...", worker); + if (!Main.isDebugging()) { + Main.startWork(AppStrings.translate("work.decompiling") + "...", worker); + } } public void hilightOffset(long offset) { @@ -477,7 +518,7 @@ public class ActionPanel extends JPanel implements SearchListener(new FlowLayout(), this); @@ -570,15 +611,37 @@ public class ActionPanel extends JPanel implements SearchListener { + if (val) { + if (hexOnlyButton.isSelected()) { + setHex(ScriptExportMode.HEX); + } else if (constantsViewButton.isSelected()) { + setHex(ScriptExportMode.CONSTANTS); + } else { + setHex(ScriptExportMode.PCODE); + } } - } - editor.setEditable(val); - saveButton.setVisible(val); - saveButton.setEnabled(false); - editButton.setVisible(!val); - cancelButton.setVisible(val); + editor.setEditable(val); + saveButton.setVisible(val); + saveButton.setEnabled(false); + editButton.setVisible(!val); + cancelButton.setVisible(val); - editor.getCaret().setVisible(true); - asmLabel.setIcon(val ? View.getIcon("editing16") : null); // this line is not working - topButtonsPan.setVisible(!val); - editMode = val; - editor.requestFocusInWindow(); + editor.getCaret().setVisible(true); + asmLabel.setIcon(val ? View.getIcon("editing16") : null); // this line is not working + topButtonsPan.setVisible(!val); + editMode = val; + editor.requestFocusInWindow(); + }); } public void setDecompiledEditMode(boolean val) { if (lastASM == null) { return; } - - int lastLine = decompiledEditor.getLine(); - int prefLines = lastASM.getPrefixLineCount(); - if (val) { - String newText = lastASM.removePrefixAndSuffix(lastDecompiled); - setDecompiledText(newText); - if (lastLine > -1) { - if (lastLine - prefLines >= 0) { - decompiledEditor.gotoLine(lastLine - prefLines + 1); + View.execInEventDispatch(() -> { + int lastLine = decompiledEditor.getLine(); + int prefLines = lastASM.getPrefixLineCount(); + if (val) { + String newText = lastASM.removePrefixAndSuffix(lastDecompiled); + setDecompiledText(lastASM.getScriptName(), newText); + if (lastLine > -1) { + if (lastLine - prefLines >= 0) { + decompiledEditor.gotoLine(lastLine - prefLines + 1); + } + } + } else { + String newText = lastDecompiled; + setDecompiledText(lastASM.getScriptName(), newText); + if (lastLine > -1) { + decompiledEditor.gotoLine(lastLine + prefLines + 1); } } - } else { - String newText = lastDecompiled; - setDecompiledText(newText); - if (lastLine > -1) { - decompiledEditor.gotoLine(lastLine + prefLines + 1); - } - } - decompiledEditor.setEditable(val); - saveDecompiledButton.setVisible(val); - saveDecompiledButton.setEnabled(false); - editDecompiledButton.setVisible(!val); - experimentalLabel.setVisible(!val); - cancelDecompiledButton.setVisible(val); + decompiledEditor.setEditable(val); + saveDecompiledButton.setVisible(val); + saveDecompiledButton.setEnabled(false); + editDecompiledButton.setVisible(!val); + experimentalLabel.setVisible(!val); + cancelDecompiledButton.setVisible(val); - decompiledEditor.getCaret().setVisible(true); - decLabel.setIcon(val ? View.getIcon("editing16") : null); - editDecompiledMode = val; - decompiledEditor.requestFocusInWindow(); + decompiledEditor.getCaret().setVisible(true); + decLabel.setIcon(val ? View.getIcon("editing16") : null); + editDecompiledMode = val; + decompiledEditor.requestFocusInWindow(); + }); } private void graphButtonActionPerformed(ActionEvent evt) { diff --git a/src/com/jpexs/decompiler/flash/gui/editor/DebuggableEditorPane.java b/src/com/jpexs/decompiler/flash/gui/editor/DebuggableEditorPane.java new file mode 100644 index 000000000..29763fea9 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/editor/DebuggableEditorPane.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2015 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui.editor; + +import com.jpexs.decompiler.flash.gui.Main; +import java.awt.Color; +import java.util.Set; +import jsyntaxpane.components.BreakPointListener; + +/** + * + * @author JPEXS + */ +public class DebuggableEditorPane extends LineMarkedEditorPane implements BreakPointListener { + + public static final Color BG_BREAKPOINT_COLOR = new Color(0xfc, 0x9d, 0x9f); + public static final Color FG_BREAKPOINT_COLOR = null; + public static final int PRIORITY_BREAKPOINT = 20; + + public static final Color BG_IP_COLOR = new Color(0xbd, 0xe6, 0xaa); + public static final Color FG_IP_COLOR = null; + public static final int PRIORITY_IP = 0; + + public static final Color BG_INVALID_BREAKPOINT_COLOR = new Color(0xdc, 0xdc, 0xd8); + public static final Color FG_INVALID_BREAKPOINT_COLOR = null; + public static final int PRIORITY_INVALID_BREAKPOINT = 10; + + protected String scriptName = null; + + public synchronized void setScriptName(String scriptName) { + this.scriptName = scriptName; + } + + @Override + public synchronized void toggled(int line) { + if (scriptName == null) { + return; + } + boolean on = Main.toggleBreakPoint(scriptName, line); + removeColorMarker(line, FG_INVALID_BREAKPOINT_COLOR, BG_INVALID_BREAKPOINT_COLOR, PRIORITY_INVALID_BREAKPOINT); + if (on) { + if (Main.isBreakPointValid(scriptName, line)) { + addColorMarker(line, FG_BREAKPOINT_COLOR, BG_BREAKPOINT_COLOR, PRIORITY_BREAKPOINT); + } else { + addColorMarker(line, FG_INVALID_BREAKPOINT_COLOR, BG_INVALID_BREAKPOINT_COLOR, PRIORITY_INVALID_BREAKPOINT); + } + } else { + removeColorMarker(line, FG_BREAKPOINT_COLOR, BG_BREAKPOINT_COLOR, PRIORITY_BREAKPOINT); + removeColorMarker(line, FG_INVALID_BREAKPOINT_COLOR, BG_INVALID_BREAKPOINT_COLOR, PRIORITY_INVALID_BREAKPOINT); + } + } + + public synchronized void refreshMarkers() { + removeColorMarkerOnAllLines(FG_BREAKPOINT_COLOR, BG_BREAKPOINT_COLOR, PRIORITY_BREAKPOINT); + removeColorMarkerOnAllLines(FG_INVALID_BREAKPOINT_COLOR, BG_INVALID_BREAKPOINT_COLOR, PRIORITY_INVALID_BREAKPOINT); + removeColorMarkerOnAllLines(FG_IP_COLOR, BG_IP_COLOR, PRIORITY_IP); + + if (scriptName == null) { + return; + } + + Set bkptLines = Main.getScriptBreakPoints(scriptName); + + for (int line : bkptLines) { + if (Main.isBreakPointValid(scriptName, line)) { + addColorMarker(line, FG_BREAKPOINT_COLOR, BG_BREAKPOINT_COLOR, PRIORITY_BREAKPOINT); + } else { + addColorMarker(line, FG_INVALID_BREAKPOINT_COLOR, BG_INVALID_BREAKPOINT_COLOR, PRIORITY_INVALID_BREAKPOINT); + } + } + int ip = Main.getIp(scriptName); + String ipPath = Main.getIpClass(); + if (ip > 0 && ipPath != null && ipPath.equals(scriptName)) { + addColorMarker(ip, FG_IP_COLOR, BG_IP_COLOR, PRIORITY_IP); + } + } + + @Override + public void setText(String t) { + super.setText(t); + refreshMarkers(); + } + + public String getScriptName() { + return scriptName; + } + +} diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index f8039e4ec..0670dc8ea 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -678,4 +678,17 @@ work.breakat = Breat at\u0020 work.halted = Debugging started, execution halted. Add breakpoints and click Continue (F5) to resume running. debuglog.header = Log -debuglog.button.clear = Clear \ No newline at end of file +debuglog.button.clear = Clear + +work.debugging.wait = Waiting for Flash debug projector to connect + +error.debug.listen = Cannot listen on port %port%. There might be other flash debugger running. + +debug.break.reason.unknown = (Unknown) +debug.break.reason.breakpoint = (Breakpoint) +debug.break.reason.watch = (Watch) +debug.break.reason.fault = (Fault) +debug.break.reason.stopRequest = (Stop request) +debug.break.reason.step = (Step) +debug.break.reason.halt = (Halt) +debug.break.reason.scriptLoaded = (Script loaded) \ 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 ee1141ddf..16ed58c8a 100644 --- a/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java +++ b/test/com/jpexs/decompiler/flash/gui/FlashPlayerTest.java @@ -84,6 +84,7 @@ import java.util.Random; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; +import org.testng.annotations.Test; /** *