From 8ceb44f25eb0b9bd20694d60b6fbcdc36206bd4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sat, 25 Nov 2023 22:25:04 +0100 Subject: [PATCH] Added Remembering breakpoints Added #2131 Breakpoint list dialog Fixed AS1/2 Debugger - script was cleared on stop button --- CHANGELOG.md | 3 + .../src/com/jpexs/decompiler/flash/SWF.java | 3 + .../flash/configuration/Configuration.java | 4 +- .../CustomConfigurationKeys.java | 5 +- .../SwfSpecificCustomConfiguration.java | 22 ++ .../flash/gui/BreakpointListDialog.java | 253 +++++++++++++ .../decompiler/flash/gui/DebugStackPanel.java | 2 +- .../decompiler/flash/gui/DebuggerHandler.java | 334 +++++++++++------- src/com/jpexs/decompiler/flash/gui/Main.java | 60 ++-- .../jpexs/decompiler/flash/gui/MainPanel.java | 62 +++- .../decompiler/flash/gui/abc/ABCPanel.java | 11 + .../decompiler/flash/gui/abc/LinkDialog.java | 15 +- .../flash/gui/action/ActionPanel.java | 12 +- .../flash/gui/graphics/breakpointlist16.png | Bin 0 -> 488 bytes .../locales/BreakpointListDialog.properties | 27 ++ .../BreakpointListDialog_cs.properties | 27 ++ .../flash/gui/locales/MainFrame.properties | 4 +- .../flash/gui/locales/MainFrame_cs.properties | 4 +- 18 files changed, 658 insertions(+), 190 deletions(-) create mode 100644 src/com/jpexs/decompiler/flash/gui/BreakpointListDialog.java create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/breakpointlist16.png create mode 100644 src/com/jpexs/decompiler/flash/gui/locales/BreakpointListDialog.properties create mode 100644 src/com/jpexs/decompiler/flash/gui/locales/BreakpointListDialog_cs.properties diff --git a/CHANGELOG.md b/CHANGELOG.md index 89c539014..c74256c59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ All notable changes to this project will be documented in this file. - Multilevel binary data unpacking is possible - [#2131] AS1/2 Debugger - show _root variable - [#2124] Copy tags to other SWFs and replace same classes / export names +- Remembering breakpoints +- [#2131] Breakpoint list dialog ### Fixed - [#2021], [#2000] Caret position in editors when using tabs and / or unicode @@ -53,6 +55,7 @@ All notable changes to this project will be documented in this file. - [#2131] Debugger - Correct walking variables tree - [#2131] Debugger - Breakpoints can be added while SWF is running (not just on pause) - AS3 Direct editation - types on instance variable values not properly resolved +- AS1/2 Debugger - script was cleared on stop button ### Changed - [#2120] Exported assets no longer take names from assigned classes if there is more than 1 assigned class 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 e38224be5..a5320a4ac 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -215,6 +215,7 @@ import java.util.Map; import java.util.Random; import java.util.Set; import java.util.TreeMap; +import java.util.concurrent.CancellationException; import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; @@ -3459,6 +3460,8 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { return list; } catch (InterruptedException ex) { throw ex; + } catch (CancellationException ex) { + throw ex; } catch (Exception ex) { logger.log(Level.SEVERE, null, ex); return new ActionList(src.getSwf().getCharset()); 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 69623ad9d..655d84b27 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 @@ -66,9 +66,7 @@ public final class Configuration { public static final Level logLevel; public static boolean showStat; - - public static final String ABC_DEPS_SEPARATOR = "{*sep*}"; - + @ConfigurationDefaultBoolean(true) @ConfigurationCategory("ui") public static ConfigurationItem openMultipleFiles = null; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/CustomConfigurationKeys.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/CustomConfigurationKeys.java index c2dec4c06..fdeb64763 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/CustomConfigurationKeys.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/CustomConfigurationKeys.java @@ -20,12 +20,13 @@ package com.jpexs.decompiler.flash.configuration; * * @author JPEXS */ -public class CustomConfigurationKeys { - +public class CustomConfigurationKeys { + public static final String KEY_LAST_SELECTED_PATH_RESOURCES = "lastSelectedPath.resources"; public static final String KEY_LAST_SELECTED_PATH_TAGLIST = "lastSelectedPath.taglist"; public static final String KEY_CHARSET = "charset"; public static final String KEY_LIBRARY = "library"; public static final String KEY_LOADED_IMPORT_ASSETS = "loadedImportAssets"; public static final String KEY_ABC_DEPENDENCIES = "abcDependencies"; + public static final String KEY_BREAKPOINTS = "breakpoints"; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/SwfSpecificCustomConfiguration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/SwfSpecificCustomConfiguration.java index 34a73f3d6..a79e861cd 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/SwfSpecificCustomConfiguration.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/SwfSpecificCustomConfiguration.java @@ -17,8 +17,11 @@ package com.jpexs.decompiler.flash.configuration; import java.io.Serializable; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.regex.Pattern; /** * @@ -29,7 +32,22 @@ public class SwfSpecificCustomConfiguration implements Serializable { private static final long serialVersionUID = 0x2acb421da57f5eb4L; private Map customData = new HashMap<>(); + + public static final String LIST_SEPARATOR = "{*sep*}"; + + public List getCustomDataAsList(String key) { + String data = getCustomData(key, ""); + String[] parts = (data + LIST_SEPARATOR).split(Pattern.quote(LIST_SEPARATOR)); + List result = new ArrayList<>(); + for (String part : parts) { + if (!part.isEmpty()) { + result.add(part); + } + } + return result; + } + public String getCustomData(String key, String defaultValue) { if (customData.containsKey(key)) { return customData.get(key); @@ -41,4 +59,8 @@ public class SwfSpecificCustomConfiguration implements Serializable { public void setCustomData(String key, String value) { customData.put(key, value); } + + public void setCustomData(String key, Iterable value) { + customData.put(key, String.join(LIST_SEPARATOR, value)); + } } diff --git a/src/com/jpexs/decompiler/flash/gui/BreakpointListDialog.java b/src/com/jpexs/decompiler/flash/gui/BreakpointListDialog.java new file mode 100644 index 000000000..0f82c4ad4 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/BreakpointListDialog.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2022-2023 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; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.ABC; +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.Image; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.table.DefaultTableModel; + +/** + * + * @author JPEXS + */ +public class BreakpointListDialog extends AppDialog { + private SWF swf; + private JTable table = new JTable(); + private List breakpointList = new ArrayList<>(); + private Timer refreshTimer = null; + private static final int REFRESH_TIMEOUT = 1000; + + + private class Breakpoint { + public String scriptName; + public int line; + + public Breakpoint(String scriptName, int line) { + this.scriptName = scriptName; + this.line = line; + } + } + + + public BreakpointListDialog(Window owner, SWF swf) { + super(owner); + + setDefaultCloseOperation(HIDE_ON_CLOSE); + setTitle(translate("dialog.title") + " - " + swf.getShortFileName()); + this.swf = swf; + table.setCellSelectionEnabled(false); + table.setRowSelectionAllowed(true); + table.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) { + int row = table.rowAtPoint(e.getPoint()); + if (table.getSelectedRow() != -1 && row != -1) { + gotoButtonActionPerformed(null); + } + } + } + }); + Container cnt = getContentPane(); + cnt.setLayout(new BorderLayout()); + cnt.add(new JScrollPane(table), BorderLayout.CENTER); + + JPanel buttonsPanel = new JPanel(new FlowLayout()); + + JButton gotoButton = new JButton(translate("button.goto")); + gotoButton.addActionListener(this::gotoButtonActionPerformed); + buttonsPanel.add(gotoButton); + + JButton removeButton = new JButton(translate("button.remove")); + removeButton.addActionListener(this::removeButtonActionPerformed); + buttonsPanel.add(removeButton); + + JButton removeAllButton = new JButton(translate("button.removeAll")); + removeAllButton.addActionListener(this::removeAllButtonActionPerformed); + buttonsPanel.add(removeAllButton); + + JButton closeButton = new JButton(translate("button.close")); + closeButton.addActionListener(this::closeButtonActionPerformed); + buttonsPanel.add(closeButton); + + cnt.add(buttonsPanel, BorderLayout.SOUTH); + + refresh(); + + setSize(500, 300); + + //View.setWindowIcon(this); + List images = new ArrayList<>(); + images.add(View.loadImage("breakpointlist16")); + setIconImages(images); + View.centerScreen(this); + } + + + + private Breakpoint getSelected() { + int row = table.getSelectedRow(); + if (row == -1) { + return null; + } + return breakpointList.get(row); + } + + private void closeButtonActionPerformed(ActionEvent evt) { + setVisible(false); + } + + private void removeButtonActionPerformed(ActionEvent evt) { + Breakpoint breakpoint = getSelected(); + if (breakpoint == null) { + return; + } + Main.getDebugHandler().removeBreakPoint(swf, breakpoint.scriptName, breakpoint.line); + refreshMarkers(); + refresh(); + } + + + private void refreshMarkers() { + if (swf.isAS3()) { + Main.getMainFrame().getPanel().getABCPanel().decompiledTextArea.refreshMarkers(); + Main.getMainFrame().getPanel().getABCPanel().detailPanel.methodTraitPanel.methodCodePanel.refreshMarkers(); + } else { + Main.getMainFrame().getPanel().getActionPanel().decompiledEditor.refreshMarkers(); + Main.getMainFrame().getPanel().getActionPanel().editor.refreshMarkers(); + } + } + + private void removeAllButtonActionPerformed(ActionEvent evt) { + Main.getDebugHandler().clearBreakPoints(swf); + refreshMarkers(); + refresh(); + } + + private void gotoButtonActionPerformed(ActionEvent evt) { + Breakpoint breakpoint = getSelected(); + if (breakpoint == null) { + return; + } + int classIndex = -1; + int traitIndex = -1; + int methodIndex = -1; + /*if (Main.getDebugHandler().getDebuggedSwf() == swf) { + int f = Main.getDebugHandler().moduleIdOf(breakpoint.scriptName); + if (f != -1) { + Integer newClassIndex = Main.getDebugHandler().moduleToClassIndex(f); + classIndex = newClassIndex == null ? -1 : newClassIndex; + Integer newMethodIndex = Main.getDebugHandler().moduleToMethodIndex(f); + methodIndex = newMethodIndex == null ? -1 : newMethodIndex; + Integer newTraitIndex = Main.getDebugHandler().moduleToTraitIndex(f);; + traitIndex = newTraitIndex == null ? -1 : newTraitIndex; + } + }*/ + Pattern abcPcodePattern = Pattern.compile("^#PCODE abc:(?[0-9]+),body:(?[0-9]+);.*"); + Matcher m = abcPcodePattern.matcher(breakpoint.scriptName); + if (m.matches()) { + int abcIndex = Integer.parseInt(m.group("abc")); + int bodyIndex = Integer.parseInt(m.group("body")); + ABC abc = swf.getAbcList().get(abcIndex).getABC(); + methodIndex = abc.bodies.get(bodyIndex).method_info; + } + + + Main.getMainFrame().getPanel().gotoScriptLine(swf, breakpoint.scriptName, breakpoint.line, classIndex, traitIndex, methodIndex, breakpoint.scriptName.startsWith("#PCODE")); + } + + public void refresh() { + DefaultTableModel defaultTableModel = new DefaultTableModel() { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + }; + defaultTableModel.addColumn(translate("breakpoint.scriptName")); + defaultTableModel.addColumn(translate("breakpoint.line")); + defaultTableModel.addColumn(translate("breakpoint.status")); + + Map> breakpoints = Main.getDebugHandler().getAllBreakPoints(swf, false); + + List newBreakpointList = new ArrayList<>(); + for (String scriptName : breakpoints.keySet()) { + for (int line : breakpoints.get(scriptName)) { + newBreakpointList.add(new Breakpoint(scriptName, line)); + String status = "unknown"; + if (Main.getDebugHandler().isBreakpointInvalid(swf, scriptName, line)) { + status = "invalid"; + } else if (Main.getDebugHandler().isBreakpointConfirmed(swf, scriptName, line)) { + status = "confirmed"; + } + defaultTableModel.addRow(new Object[]{scriptName, line, translate("breakpoint.status." + status)}); + } + } + breakpointList = newBreakpointList; + int selectedRow = table.getSelectedRow(); + table.setModel(defaultTableModel); + if (selectedRow >= 0 && selectedRow < defaultTableModel.getRowCount()) { + table.setRowSelectionInterval(selectedRow, selectedRow); + } + table.getColumnModel().getColumn(1).setMaxWidth(50); + table.getColumnModel().getColumn(2).setMaxWidth(75); + } + + @Override + public void setVisible(boolean b) { + if (refreshTimer != null) { + refreshTimer.cancel(); + refreshTimer = null; + } + if (b) { + refreshTimer = new Timer(); + refreshTimer.schedule(new TimerTask() { + @Override + public void run() { + View.execInEventDispatch(new Runnable() { + @Override + public void run() { + refresh(); + } + }); + } + + }, REFRESH_TIMEOUT, REFRESH_TIMEOUT); + } + super.setVisible(b); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/DebugStackPanel.java b/src/com/jpexs/decompiler/flash/gui/DebugStackPanel.java index 0c0fc8ddc..2ec177fec 100644 --- a/src/com/jpexs/decompiler/flash/gui/DebugStackPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/DebugStackPanel.java @@ -95,7 +95,7 @@ public class DebugStackPanel extends JPanel { String scriptName = (String) stackTable.getModel().getValueAt(row, 0); int line = (int) (Integer) stackTable.getModel().getValueAt(row, 1); Main.getMainFrame().getPanel().gotoScriptLine(Main.getMainFrame().getPanel().getCurrentSwf(), - scriptName, line, classIndices[row], traitIndices[row], methodIndices[row]); + scriptName, line, classIndices[row], traitIndices[row], methodIndices[row], Main.isDebugPCode()); Main.getDebugHandler().setDepth(row); } } diff --git a/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java b/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java index ee7e7db3f..abd1bbe5c 100644 --- a/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java +++ b/src/com/jpexs/decompiler/flash/gui/DebuggerHandler.java @@ -51,6 +51,7 @@ import com.jpexs.debugger.flash.messages.out.OutPlay; import com.jpexs.debugger.flash.messages.out.OutProcessedTag; import com.jpexs.debugger.flash.messages.out.OutRewind; import com.jpexs.debugger.flash.messages.out.OutSwfInfo; +import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.graph.DottedChain; import java.io.IOException; @@ -60,8 +61,8 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.WeakHashMap; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.logging.ConsoleHandler; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -93,13 +94,13 @@ public class DebuggerHandler implements DebugConnectionListener { private Map moduleToMethodIndex = new HashMap<>(); - private Map> toAddBPointMap = new HashMap<>(); + private Map>> toAddBPointMap = new WeakHashMap<>(); - private Map> confirmedPointMap = new HashMap<>(); + private Map>> confirmedPointMap = new WeakHashMap<>(); - private Map> invalidBreakPointMap = new HashMap<>(); + private Map>> invalidBreakPointMap = new WeakHashMap<>(); - private Map> toRemoveBPointMap = new HashMap<>(); + private Map>> toRemoveBPointMap = new WeakHashMap<>(); private int breakIp = -1; @@ -108,6 +109,8 @@ public class DebuggerHandler implements DebugConnectionListener { private List stackScriptNames = new ArrayList<>(); private List stackLines = new ArrayList<>(); + + private SWF debuggedSwf = null; public static class ActionScriptException extends Exception { @@ -127,6 +130,14 @@ public class DebuggerHandler implements DebugConnectionListener { } } + public void setDebuggedSwf(SWF debuggedSwf) { + this.debuggedSwf = debuggedSwf; + } + + public SWF getDebuggedSwf() { + return debuggedSwf; + } + public int getBreakIp() { if (!isPaused()) { return -1; @@ -193,24 +204,27 @@ public class DebuggerHandler implements DebugConnectionListener { } } - public synchronized void removeBreakPoint(String scriptName, int line) { - if (isBreakpointInvalid(scriptName, line)) { - invalidBreakPointMap.get(scriptName).remove(line); - if (invalidBreakPointMap.get(scriptName).isEmpty()) { - invalidBreakPointMap.remove(scriptName); + public synchronized void removeBreakPoint(SWF swf, String scriptName, int line) { + if (isBreakpointInvalid(swf, scriptName, line)) { + invalidBreakPointMap.get(swf).get(scriptName).remove(line); + if (invalidBreakPointMap.get(swf).get(scriptName).isEmpty()) { + invalidBreakPointMap.get(swf).remove(scriptName); } return; } - if (isBreakpointToAdd(scriptName, line)) { - toAddBPointMap.get(scriptName).remove(line); - if (toAddBPointMap.get(scriptName).isEmpty()) { - toAddBPointMap.remove(scriptName); + if (isBreakpointToAdd(swf, scriptName, line)) { + toAddBPointMap.get(swf).get(scriptName).remove(line); + if (toAddBPointMap.get(swf).get(scriptName).isEmpty()) { + toAddBPointMap.get(swf).remove(scriptName); } - } else if (isBreakpointConfirmed(scriptName, line)) { - if (!toRemoveBPointMap.containsKey(scriptName)) { - toRemoveBPointMap.put(scriptName, new TreeSet<>()); + } else if (isBreakpointConfirmed(swf, scriptName, line)) { + if (!toRemoveBPointMap.containsKey(swf)) { + toRemoveBPointMap.put(swf, new HashMap<>()); } - toRemoveBPointMap.get(scriptName).add(line); + if (!toRemoveBPointMap.get(swf).containsKey(scriptName)) { + toRemoveBPointMap.get(swf).put(scriptName, new TreeSet<>()); + } + toRemoveBPointMap.get(swf).get(scriptName).add(line); } try { sendBreakPoints(); @@ -230,86 +244,114 @@ public class DebuggerHandler implements DebugConnectionListener { } } - public synchronized Set getBreakPoints(String scriptName, boolean onlyValid) { - Set lines = new TreeSet<>(); - if (confirmedPointMap.containsKey(scriptName)) { - lines.addAll(confirmedPointMap.get(scriptName)); + public synchronized Set getBreakPoints(SWF swf, String scriptName, boolean onlyValid) { + Set lines = new TreeSet<>(); + if (confirmedPointMap.containsKey(swf) && confirmedPointMap.get(swf).containsKey(scriptName)) { + lines.addAll(confirmedPointMap.get(swf).get(scriptName)); } - if (toAddBPointMap.containsKey(scriptName)) { - lines.addAll(toAddBPointMap.get(scriptName)); + if (toAddBPointMap.containsKey(swf) && toAddBPointMap.get(swf).containsKey(scriptName)) { + lines.addAll(toAddBPointMap.get(swf).get(scriptName)); } - if (!onlyValid && invalidBreakPointMap.containsKey(scriptName)) { - lines.addAll(invalidBreakPointMap.get(scriptName)); + if (!onlyValid && invalidBreakPointMap.containsKey(swf) && invalidBreakPointMap.get(swf).containsKey(scriptName)) { + lines.addAll(invalidBreakPointMap.get(swf).get(scriptName)); } return lines; } - public synchronized void clearBreakPoints() { - for (String scriptName : confirmedPointMap.keySet()) { - if (!toAddBPointMap.containsKey(scriptName)) { - toAddBPointMap.put(scriptName, new TreeSet<>()); + public synchronized void clearBreakPoints(SWF swf) { + Map> breakpoints = getAllBreakPoints(swf, false); + for (String scriptName : breakpoints.keySet()) { + for (int line : breakpoints.get(scriptName)) { + removeBreakPoint(swf, scriptName, line); } - toAddBPointMap.get(scriptName).addAll(confirmedPointMap.get(scriptName)); } - for (String scriptName : invalidBreakPointMap.keySet()) { - if (!toAddBPointMap.containsKey(scriptName)) { - toAddBPointMap.put(scriptName, new TreeSet<>()); - } - toAddBPointMap.get(scriptName).addAll(invalidBreakPointMap.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<>()); + public synchronized void makeBreakPointsUnconfirmed(SWF swf) { + if (confirmedPointMap.containsKey(swf)) { + for (String scriptName : confirmedPointMap.get(swf).keySet()) { + if (!toAddBPointMap.containsKey(swf)) { + toAddBPointMap.put(swf, new HashMap<>()); + } + if (!toAddBPointMap.get(swf).containsKey(scriptName)) { + toAddBPointMap.get(swf).put(scriptName, new TreeSet<>()); + } + toAddBPointMap.get(swf).get(scriptName).addAll(confirmedPointMap.get(swf).get(scriptName)); } - ret.get(scriptName).addAll(toAddBPointMap.get(scriptName)); + confirmedPointMap.get(swf).clear(); } - if (!validOnly) { - for (String scriptName : invalidBreakPointMap.keySet()) { + if (invalidBreakPointMap.containsKey(swf)) { + for (String scriptName : invalidBreakPointMap.get(swf).keySet()) { + if (!toAddBPointMap.containsKey(swf)) { + toAddBPointMap.put(swf, new HashMap<>()); + } + if (!toAddBPointMap.get(swf).containsKey(scriptName)) { + toAddBPointMap.get(swf).put(scriptName, new TreeSet<>()); + } + toAddBPointMap.get(swf).get(scriptName).addAll(invalidBreakPointMap.get(swf).get(scriptName)); + } + + invalidBreakPointMap.get(swf).clear(); + } + + } + + public synchronized Map> getAllBreakPoints(SWF swf, boolean validOnly) { + Map> ret = new HashMap<>(); + if (confirmedPointMap.containsKey(swf)) { + for (String scriptName : confirmedPointMap.get(swf).keySet()) { + Set lines = new TreeSet<>(); + lines.addAll(confirmedPointMap.get(swf).get(scriptName)); + ret.put(scriptName, lines); + } + } + if (toAddBPointMap.containsKey(swf)) { + for (String scriptName : toAddBPointMap.get(swf).keySet()) { if (!ret.containsKey(scriptName)) { ret.put(scriptName, new TreeSet<>()); } - ret.get(scriptName).addAll(invalidBreakPointMap.get(scriptName)); + ret.get(scriptName).addAll(toAddBPointMap.get(swf).get(scriptName)); + } + } + if (!validOnly) { + if (invalidBreakPointMap.containsKey(swf)) { + for (String scriptName : invalidBreakPointMap.get(swf).keySet()) { + if (!ret.containsKey(scriptName)) { + ret.put(scriptName, new TreeSet<>()); + } + ret.get(scriptName).addAll(invalidBreakPointMap.get(swf).get(scriptName)); + } } } return ret; } - public boolean addBreakPoint(String scriptName, int line) { + public boolean addBreakPoint(SWF swf, String scriptName, int line) { synchronized (this) { - Logger.getLogger(DebuggerHandler.class - .getName()).log(Level.FINE, "adding bp {0}:{1}", new Object[]{scriptName, line}); - if (isBreakpointToRemove(scriptName, line)) { - toRemoveBPointMap.get(scriptName).remove(line); - if (toRemoveBPointMap.get(scriptName).isEmpty()) { - toRemoveBPointMap.remove(scriptName); - + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINE, "adding bp {0}:{1}", new Object[]{scriptName, line}); + if (isBreakpointToRemove(swf, scriptName, line)) { + toRemoveBPointMap.get(swf).get(scriptName).remove(line); + if (toRemoveBPointMap.get(swf).get(scriptName).isEmpty()) { + toRemoveBPointMap.get(swf).remove(scriptName); } } - if (isBreakpointConfirmed(scriptName, line)) { + if (isBreakpointConfirmed(swf, scriptName, line)) { Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINE, "bp {0}:{1} already confirmed", new Object[]{scriptName, line}); return true; } - if (isBreakpointInvalid(scriptName, line)) { + if (isBreakpointInvalid(swf, scriptName, line)) { Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINE, "bp {0}:{1} already invalid", new Object[]{scriptName, line}); return false; } - if (!toAddBPointMap.containsKey(scriptName)) { - toAddBPointMap.put(scriptName, new TreeSet<>()); + if (!toAddBPointMap.containsKey(swf)) { + toAddBPointMap.put(swf, new HashMap<>()); } - toAddBPointMap.get(scriptName).add(line); + if (!toAddBPointMap.get(swf).containsKey(scriptName)) { + toAddBPointMap.get(swf).put(scriptName, new TreeSet<>()); + } + toAddBPointMap.get(swf).get(scriptName).add(line); Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINE, "bp {0}:{1} added to todo", new Object[]{scriptName, line}); } try { @@ -321,27 +363,30 @@ public class DebuggerHandler implements DebugConnectionListener { return true; } - public synchronized boolean isBreakpointConfirmed(String scriptName, int line) { - return confirmedPointMap.containsKey(scriptName) && confirmedPointMap.get(scriptName).contains(line); + public synchronized boolean isBreakpointConfirmed(SWF swf, String scriptName, int line) { + return confirmedPointMap.containsKey(swf) && confirmedPointMap.get(swf).containsKey(scriptName) && confirmedPointMap.get(swf).get(scriptName).contains(line); } - public synchronized boolean isBreakpointToAdd(String scriptName, int line) { - return toAddBPointMap.containsKey(scriptName) && toAddBPointMap.get(scriptName).contains(line); + public synchronized boolean isBreakpointToAdd(SWF swf, String scriptName, int line) { + return toAddBPointMap.containsKey(swf) && toAddBPointMap.get(swf).containsKey(scriptName) && toAddBPointMap.get(swf).get(scriptName).contains(line); } - public synchronized boolean isBreakpointToRemove(String scriptName, int line) { - return toRemoveBPointMap.containsKey(scriptName) && toRemoveBPointMap.get(scriptName).contains(line); + public synchronized boolean isBreakpointToRemove(SWF swf, String scriptName, int line) { + return toRemoveBPointMap.containsKey(swf) && toRemoveBPointMap.get(swf).containsKey(scriptName) && toRemoveBPointMap.get(swf).get(scriptName).contains(line); } - public synchronized boolean isBreakpointInvalid(String scriptName, int line) { - return invalidBreakPointMap.containsKey(scriptName) && invalidBreakPointMap.get(scriptName).contains(line); + public synchronized boolean isBreakpointInvalid(SWF swf, String scriptName, int line) { + return invalidBreakPointMap.containsKey(swf) && invalidBreakPointMap.get(swf).containsKey(scriptName) && invalidBreakPointMap.get(swf).get(scriptName).contains(line); } - private synchronized void markBreakPointInvalid(String scriptName, int line) { - if (!invalidBreakPointMap.containsKey(scriptName)) { - invalidBreakPointMap.put(scriptName, new TreeSet<>()); + private synchronized void markBreakPointInvalid(SWF swf, String scriptName, int line) { + if (!invalidBreakPointMap.containsKey(swf)) { + invalidBreakPointMap.put(swf, new HashMap<>()); } - invalidBreakPointMap.get(scriptName).add(line); + if (!invalidBreakPointMap.get(swf).containsKey(scriptName)) { + invalidBreakPointMap.get(swf).put(scriptName, new TreeSet<>()); + } + invalidBreakPointMap.get(swf).get(scriptName).add(line); } private InFrame frame; @@ -523,20 +568,31 @@ public class DebuggerHandler implements DebugConnectionListener { } commands = null; synchronized (this) { - for (String scriptName : confirmedPointMap.keySet()) { - if (!toAddBPointMap.containsKey(scriptName)) { - toAddBPointMap.put(scriptName, new TreeSet<>()); + if (confirmedPointMap.containsKey(debuggedSwf)) { + for (String scriptName : confirmedPointMap.get(debuggedSwf).keySet()) { + if (!toAddBPointMap.containsKey(debuggedSwf)) { + toAddBPointMap.put(debuggedSwf, new HashMap<>()); + } + if (!toAddBPointMap.get(debuggedSwf).containsKey(scriptName)) { + toAddBPointMap.get(debuggedSwf).put(scriptName, new TreeSet<>()); + } + toAddBPointMap.get(debuggedSwf).get(scriptName).addAll(confirmedPointMap.get(debuggedSwf).get(scriptName)); } - toAddBPointMap.get(scriptName).addAll(confirmedPointMap.get(scriptName)); + confirmedPointMap.get(debuggedSwf).clear(); } - confirmedPointMap.clear(); - for (String scriptName : invalidBreakPointMap.keySet()) { - if (!toAddBPointMap.containsKey(scriptName)) { - toAddBPointMap.put(scriptName, new TreeSet<>()); + + if (invalidBreakPointMap.containsKey(debuggedSwf)) { + for (String scriptName : invalidBreakPointMap.get(debuggedSwf).keySet()) { + if (!toAddBPointMap.containsKey(debuggedSwf)) { + toAddBPointMap.put(debuggedSwf, new HashMap<>()); + } + if (!toAddBPointMap.get(debuggedSwf).containsKey(scriptName)) { + toAddBPointMap.get(debuggedSwf).put(scriptName, new TreeSet<>()); + } + toAddBPointMap.get(debuggedSwf).get(scriptName).addAll(invalidBreakPointMap.get(debuggedSwf).get(scriptName)); } - toAddBPointMap.get(scriptName).addAll(invalidBreakPointMap.get(scriptName)); - } - invalidBreakPointMap.clear(); + invalidBreakPointMap.get(debuggedSwf).clear(); + } } for (ConnectionListener l : clisteners) { l.disconnected(); @@ -552,17 +608,7 @@ public class DebuggerHandler implements DebugConnectionListener { throw new IOException("Not connected"); } return commands; - } - - private static void enlog(Class cls) { - Level level = Level.FINEST; - - Logger mylog = Logger.getLogger(cls.getName()); - mylog.setLevel(level); - ConsoleHandler ch = new ConsoleHandler(); - ch.setLevel(level); - mylog.addHandler(ch); - } + } @Override public void failedListen(IOException ex) { @@ -581,7 +627,7 @@ public class DebuggerHandler implements DebugConnectionListener { @Override public void connected(DebuggerConnection con) { - clearBreakPoints(); + makeBreakPointsUnconfirmed(debuggedSwf); Main.startWork(AppStrings.translate("work.debugging"), null); @@ -655,6 +701,8 @@ public class DebuggerHandler implements DebugConnectionListener { name = name.replaceAll("\\[(invalid_utf8=[0-9]+)\\]", "{$1}"); + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINE, "Received script added - index {0} name: {1}", new Object[]{file, name}); + Matcher m; if ((m = patAS3.matcher(name)).matches()) { String clsNameWithSuffix = m.group(3).replace("{{semicolon}}", ";"); @@ -734,16 +782,21 @@ public class DebuggerHandler implements DebugConnectionListener { 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 (!confirmedPointMap.containsKey(debuggedSwf)) { + confirmedPointMap.put(debuggedSwf, new HashMap<>()); } - if (toAddBPointMap.containsKey(sname)) { - toAddBPointMap.get(sname).remove(isb.lines.get(i)); - if (toAddBPointMap.get(sname).isEmpty()) { - toAddBPointMap.remove(sname); + if (!confirmedPointMap.get(debuggedSwf).containsKey(sname)) { + confirmedPointMap.get(debuggedSwf).put(sname, new TreeSet<>()); + } + if (toAddBPointMap.containsKey(debuggedSwf)) { + if (toAddBPointMap.get(debuggedSwf).containsKey(sname)) { + toAddBPointMap.get(debuggedSwf).get(sname).remove(isb.lines.get(i)); + if (toAddBPointMap.get(debuggedSwf).get(sname).isEmpty()) { + toAddBPointMap.get(debuggedSwf).remove(sname); + } } } - confirmedPointMap.get(sname).add(isb.lines.get(i)); + confirmedPointMap.get(debuggedSwf).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)}); } } @@ -880,46 +933,53 @@ public class DebuggerHandler implements DebugConnectionListener { Logger.getLogger(DebuggerHandler.class.getName()).log(Level.FINEST, "not sending bps, not connected"); 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); + synchronized (this) { + if (toRemoveBPointMap.containsKey(debuggedSwf)) { + for (String scriptName : toRemoveBPointMap.get(debuggedSwf).keySet()) { + int file = moduleIdOf(scriptName); + if (file > -1) { + for (int line : toRemoveBPointMap.get(debuggedSwf).get(scriptName)) { + if (isBreakpointConfirmed(debuggedSwf, scriptName, line)) { + commands.removeBreakPoint(file, line); + confirmedPointMap.get(debuggedSwf).get(scriptName).remove(line); + if (confirmedPointMap.get(debuggedSwf).get(scriptName).isEmpty()) { + confirmedPointMap.get(debuggedSwf).remove(scriptName); + } } + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "Breakpoint {0}:{1} removed", new Object[]{scriptName, line}); } - Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "Breakpoint {0}:{1} removed", new Object[]{scriptName, line}); } } + toRemoveBPointMap.clear(); } - 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<>()); + if (toAddBPointMap.containsKey(debuggedSwf)) { + for (String scriptName : toAddBPointMap.get(debuggedSwf).keySet()) { + int file = moduleIdOf(scriptName); + if (file > -1) { + for (int line : toAddBPointMap.get(debuggedSwf).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(debuggedSwf)) { + confirmedPointMap.put(debuggedSwf, new HashMap<>()); + } + if (!confirmedPointMap.get(debuggedSwf).containsKey(scriptName)) { + confirmedPointMap.get(debuggedSwf).put(scriptName, new TreeSet<>()); + } + confirmedPointMap.get(debuggedSwf).get(scriptName).add(line); + } else { + Logger.getLogger(DebuggerHandler.class.getName()).log(Level.INFO, "Breakpoint {0}:{1} unable to submit", new Object[]{scriptName, line}); + markBreakPointInvalid(debuggedSwf, scriptName, line); } - 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); + } + } else { + for (int line : toAddBPointMap.get(debuggedSwf).get(scriptName)) { + markBreakPointInvalid(debuggedSwf, scriptName, line); } } - } else { - for (int line : toAddBPointMap.get(scriptName)) { - markBreakPointInvalid(scriptName, line); - } } + toAddBPointMap.clear(); } - 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 158f2daa6..f12a9e1d7 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -647,6 +647,7 @@ public class Main { if (swf == null) { return; } + debugHandler.setDebuggedSwf(swf); File tempFile = null; try { @@ -726,33 +727,39 @@ public class Main { } public static synchronized boolean isBreakPointValid(String scriptName, int line) { - return !getDebugHandler().isBreakpointInvalid(scriptName, line); + SWF swf = getMainFrame().getPanel().getCurrentSwf(); + return !getDebugHandler().isBreakpointInvalid(swf, scriptName, line); } public static synchronized void addBreakPoint(String scriptName, int line) { - getDebugHandler().addBreakPoint(scriptName, line); + SWF swf = getMainFrame().getPanel().getCurrentSwf(); + getDebugHandler().addBreakPoint(swf, scriptName, line); } public static synchronized void removeBreakPoint(String scriptName, int line) { - getDebugHandler().removeBreakPoint(scriptName, line); + SWF swf = getMainFrame().getPanel().getCurrentSwf(); + getDebugHandler().removeBreakPoint(swf, scriptName, line); } public static synchronized boolean toggleBreakPoint(String scriptName, int line) { - if (getDebugHandler().isBreakpointToAdd(scriptName, line) || getDebugHandler().isBreakpointConfirmed(scriptName, line) || getDebugHandler().isBreakpointInvalid(scriptName, line)) { - getDebugHandler().removeBreakPoint(scriptName, line); + SWF swf = getMainFrame().getPanel().getCurrentSwf(); + if (getDebugHandler().isBreakpointToAdd(swf, scriptName, line) || getDebugHandler().isBreakpointConfirmed(swf, scriptName, line) || getDebugHandler().isBreakpointInvalid(swf, scriptName, line)) { + getDebugHandler().removeBreakPoint(swf, scriptName, line); return false; } else { - getDebugHandler().addBreakPoint(scriptName, line); + getDebugHandler().addBreakPoint(swf, scriptName, line); return true; } } public static synchronized Map> getPackBreakPoints(boolean validOnly) { - return getDebugHandler().getAllBreakPoints(validOnly); + SWF swf = getMainFrame().getPanel().getCurrentSwf(); + return getDebugHandler().getAllBreakPoints(swf, validOnly); } public static synchronized Set getScriptBreakPoints(String pack, boolean onlyValid) { - return getDebugHandler().getBreakPoints(pack, onlyValid); + SWF swf = getMainFrame().getPanel().getCurrentSwf(); + return getDebugHandler().getBreakPoints(swf, pack, onlyValid); } public static DebuggerHandler getDebugHandler() { @@ -1640,15 +1647,8 @@ public class Main { SWF swf = (SWF) openable; SwfSpecificCustomConfiguration conf = Configuration.getSwfSpecificCustomConfiguration(swf.getShortPathTitle()); if (conf != null) { - String abcDependencies = conf.getCustomData(CustomConfigurationKeys.KEY_ABC_DEPENDENCIES, ""); - if (!abcDependencies.isEmpty()) { - String[] parts = (abcDependencies + Configuration.ABC_DEPS_SEPARATOR).split(Pattern.quote(Configuration.ABC_DEPS_SEPARATOR)); - List preselectedNames = new ArrayList<>(); - for (String part : parts) { - if (!part.isEmpty()) { - preselectedNames.add(part); - } - } + List preselectedNames = conf.getCustomDataAsList(CustomConfigurationKeys.KEY_ABC_DEPENDENCIES); + if (!preselectedNames.isEmpty()) { swf.setAbcIndexDependencies(namesToSwfs(preselectedNames)); if (mainFrame != null && mainFrame.getPanel() != null && mainFrame.getPanel().getABCPanel() != null) { mainFrame.getPanel().getABCPanel().updateLinksLabel(); @@ -1687,6 +1687,14 @@ public class Main { if (swfCustomConf != null) { resourcesPathStr = swfCustomConf.getCustomData(CustomConfigurationKeys.KEY_LAST_SELECTED_PATH_RESOURCES, resourcesPathStr); tagListPathStr = swfCustomConf.getCustomData(CustomConfigurationKeys.KEY_LAST_SELECTED_PATH_TAGLIST, null); + List breakpointsList = swfCustomConf.getCustomDataAsList(CustomConfigurationKeys.KEY_BREAKPOINTS); + for (String breakpoint : breakpointsList) { + if (breakpoint.matches("^.*:[0-9]+$")) { + int line = Integer.parseInt(breakpoint.substring(breakpoint.lastIndexOf(":") + 1)); + String scriptName = breakpoint.substring(0, breakpoint.lastIndexOf(":")); + getDebugHandler().addBreakPoint(fswf, scriptName, line); + } + } } if (isInited()) { @@ -2457,7 +2465,7 @@ public class Main { View.execInEventDispatch(new Runnable() { @Override public void run() { - mainFrame.getPanel().gotoScriptLine(getMainFrame().getPanel().getCurrentSwf(), scriptName, line, classIndex, traitIndex, methodIndex); + mainFrame.getPanel().gotoScriptLine(getMainFrame().getPanel().getCurrentSwf(), scriptName, line, classIndex, traitIndex, methodIndex, Main.isDebugPCode()); } }); } @@ -3315,18 +3323,16 @@ public class Main { SwfSpecificCustomConfiguration conf = Configuration.getSwfSpecificCustomConfiguration(swf.getShortPathTitle()); List dependencies = new ArrayList<>(); if (conf != null) { - String abcDependencies = conf.getCustomData(CustomConfigurationKeys.KEY_ABC_DEPENDENCIES, ""); - if (!abcDependencies.isEmpty()) { - String[] parts = (abcDependencies + Configuration.ABC_DEPS_SEPARATOR).split(Pattern.quote(Configuration.ABC_DEPS_SEPARATOR)); - List preselectedNames = new ArrayList<>(); - for (String part : parts) { - if (!part.isEmpty()) { - preselectedNames.add(part); - } - } + List preselectedNames = conf.getCustomDataAsList(CustomConfigurationKeys.KEY_ABC_DEPENDENCIES); + if (!preselectedNames.isEmpty()) { dependencies = Main.namesToSwfs(preselectedNames); } } return dependencies; } + + public static void showBreakpointsList() { + SWF swf = getMainFrame().getPanel().getCurrentSwf(); + getMainFrame().getPanel().showBreakpointlistDialog(swf); + } } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index df4cdfa1c..f0a2cc57e 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -453,6 +453,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public ScrollPosStorage scrollPosStorage; private Map abcExplorerDialogs = new WeakHashMap<>(); + + private Map breakpointsListDialogs = new WeakHashMap<>(); + public void savePins() { pinsPanel.save(); @@ -1685,7 +1688,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (abcExportDialog != null) { abcExportDialog.setVisible(false); abcExplorerDialogs.remove(openable); - } + } } } for (Openable openable : openableList) { @@ -1697,11 +1700,17 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se for (SWF swf : swfsToClose) { swf.clearTagSwfs(); + saveBreakpoints(swf); ABCExplorerDialog abcExportDialog = abcExplorerDialogs.get(swf); if (abcExportDialog != null) { abcExportDialog.setVisible(false); abcExplorerDialogs.remove(swf); } + BreakpointListDialog breakpointsListDialog = breakpointsListDialogs.get(swf); + if (breakpointsListDialog != null) { + breakpointsListDialog.setVisible(false); + breakpointsListDialogs.remove(swf); + } if (!onExit) { SwfSpecificCustomConfiguration cc = Configuration.getSwfSpecificCustomConfiguration(swf.getShortPathTitle()); if (cc != null) { @@ -1771,11 +1780,19 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (cc != null) { cc.setCustomData(CustomConfigurationKeys.KEY_LOADED_IMPORT_ASSETS, ""); } + + saveBreakpoints(swf); ABCExplorerDialog abcExportDialog = abcExplorerDialogs.get(swf); if (abcExportDialog != null) { abcExportDialog.setVisible(false); } + + BreakpointListDialog breakpointsListDialog = breakpointsListDialogs.get(swf); + if (breakpointsListDialog != null) { + breakpointsListDialog.setVisible(false); + breakpointsListDialogs.remove(swf); + } } openables.remove(openableList); @@ -2596,7 +2613,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se gotoScriptName(swf, scriptName); } - public void gotoScriptLine(SWF swf, String scriptName, int line, int classIndex, int traitIndex, int methodIndex) { + public void gotoScriptLine(SWF swf, String scriptName, int line, int classIndex, int traitIndex, int methodIndex, boolean pcode) { View.checkAccess(); if (abcPanel != null && swf.isAS3()) { @@ -2607,7 +2624,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se View.execInEventDispatchLater(new Runnable() { @Override public void run() { - if (Main.isDebugPCode()) { + if (pcode) { if (classIndex != -1) { if (abcPanel.decompiledTextArea.getClassIndex() != classIndex) { abcPanel.decompiledTextArea.setClassIndex(classIndex); @@ -2638,7 +2655,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se View.execInEventDispatchLater(new Runnable() { @Override public void run() { - if (Main.isDebugPCode()) { + if (pcode) { actionPanel.editor.gotoLine(line); } else { actionPanel.decompiledEditor.gotoLine(line); @@ -5613,10 +5630,25 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se showCard(CARDEMPTYPANEL); } - public void reload(boolean forceReload) { - reload(forceReload, true); + private void saveBreakpoints(Openable openable) { + if (openable instanceof SWF) { + SwfSpecificCustomConfiguration swfCustomConf = Configuration.getOrCreateSwfSpecificCustomConfiguration(openable.getShortPathTitle()); + SWF swf = (SWF) openable; + Map> breakpoints = Main.getDebugHandler().getAllBreakPoints(swf, false); + List breakpointList = new ArrayList<>(); + for (String scriptName : breakpoints.keySet()) { + for (int line : breakpoints.get(scriptName)) { + breakpointList.add(scriptName + ":" + line); + } + } + swfCustomConf.setCustomData(CustomConfigurationKeys.KEY_BREAKPOINTS, breakpointList); + } } + public void reload(boolean forceReload) { + reload(forceReload, true); + } + public void reload(boolean forceReload, boolean scrollToVisible) { View.checkAccess(); @@ -5656,6 +5688,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se SwfSpecificCustomConfiguration swfCustomConf = Configuration.getOrCreateSwfSpecificCustomConfiguration(openable.getShortPathTitle()); swfCustomConf.setCustomData(CustomConfigurationKeys.KEY_LAST_SELECTED_PATH_RESOURCES, tagTree.getSelectionPathString()); swfCustomConf.setCustomData(CustomConfigurationKeys.KEY_LAST_SELECTED_PATH_TAGLIST, tagListTree.getSelectionPathString()); + saveBreakpoints(openable); } } @@ -6501,6 +6534,23 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } return dialog; } + + public BreakpointListDialog showBreakpointlistDialog(SWF swf) { + BreakpointListDialog dialog = breakpointsListDialogs.get(swf); + if (dialog != null) { + dialog.refresh(); + if (!dialog.isVisible()) { + dialog.setVisible(true); + } else { + dialog.toFront(); + } + } else { + dialog = new BreakpointListDialog(mainFrame.getWindow(), swf); + breakpointsListDialogs.put(swf, dialog); + dialog.setVisible(true); + } + return dialog; + } public boolean fontEmbed(TreeItem item, boolean create) { return previewPanel.getFontPanel().fontEmbed(item, create); diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java index 9a5dfab3f..ac40d57b3 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java @@ -51,6 +51,7 @@ import com.jpexs.decompiler.flash.configuration.SwfSpecificCustomConfiguration; import com.jpexs.decompiler.flash.ecma.EcmaScript; import com.jpexs.decompiler.flash.gui.AppDialog; import com.jpexs.decompiler.flash.gui.AppStrings; +import com.jpexs.decompiler.flash.gui.BreakpointListDialog; import com.jpexs.decompiler.flash.gui.DebugPanel; import com.jpexs.decompiler.flash.gui.DebuggerHandler; import com.jpexs.decompiler.flash.gui.FasterScrollPane; @@ -962,6 +963,12 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener selectedSWFs = new ArrayList<>(); if (conf != null) { - String abcDependencies = conf.getCustomData(CustomConfigurationKeys.KEY_ABC_DEPENDENCIES, ""); - if (!abcDependencies.isEmpty()) { - String[] parts = (abcDependencies + Configuration.ABC_DEPS_SEPARATOR).split(Pattern.quote(Configuration.ABC_DEPS_SEPARATOR)); - List preselectedNames = new ArrayList<>(); - for (String part : parts) { - if (!part.isEmpty()) { - preselectedNames.add(part); - } - } + List preselectedNames = conf.getCustomDataAsList(CustomConfigurationKeys.KEY_ABC_DEPENDENCIES); + if (!preselectedNames.isEmpty()) { selectedSWFs = Main.namesToSwfs(preselectedNames); } } @@ -142,8 +135,8 @@ public class LinkDialog extends JDialog { public void save(SWF swf, boolean force) { Map map = getSelectedSwfs(); SwfSpecificCustomConfiguration conf = Configuration.getOrCreateSwfSpecificCustomConfiguration(swf.getShortPathTitle()); - String oldValue = conf.getCustomData(CustomConfigurationKeys.KEY_ABC_DEPENDENCIES, ""); - String newValue = String.join(Configuration.ABC_DEPS_SEPARATOR, map.keySet()); + List oldValue = conf.getCustomDataAsList(CustomConfigurationKeys.KEY_ABC_DEPENDENCIES); + List newValue = new ArrayList<>(map.keySet()); conf.setCustomData(CustomConfigurationKeys.KEY_ABC_DEPENDENCIES, newValue); List swfs = new ArrayList<>(map.values()); if (!Objects.equals(oldValue, newValue) || force) { diff --git a/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java b/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java index 281fe2c52..f7148010c 100644 --- a/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java @@ -559,7 +559,7 @@ public class ActionPanel extends JPanel implements SearchListener { @@ -934,6 +934,12 @@ public class ActionPanel extends JPanel implements SearchListenerP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10d+}4 zK~y-6#ge^Cn{gb(Kff5LL?x-{vq+3>V`&9n;viITQK(vL8o|v!q4Y%%mw<@71SB~5 zvbZ>R?^G4h5<$d846Pz022x{q#3bgr)G^`dAb#P-yZhd8MiQYw;L8ikid@;YeUwVg zTx2rGI6QaIvPR}UMCPa7^!x70WhR&7EykD-;0In-;1z)#Ndh%HCxLowL zJ!}4!NIV55fcI6*KLf`Eyx!jEc=)2YvZnM>T!XJVen`S8j3l00)JWuQkAu2QL-{bSpA_ky{bn<7w@G%d+gfJ@Q=K+R?T`sd-< zd0#4*tJ~b$O(&A4@$+nU%V`4huSAS7O~AFJTgUnD1s-b3tw4JPUIEyZbX#r85vou5 eD{%DqQ~nEQGDa^bvo(1D0000. + +dialog.title = List of breakpoints +button.goto = Go to +button.remove = Remove +button.removeAll = Remove all +button.close = Close + +breakpoint.scriptName = Script name +breakpoint.line = Line +breakpoint.status = Status +breakpoint.status.invalid = invalid +breakpoint.status.confirmed = confirmed +breakpoint.status.unknown = unknown diff --git a/src/com/jpexs/decompiler/flash/gui/locales/BreakpointListDialog_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/BreakpointListDialog_cs.properties new file mode 100644 index 000000000..596086062 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/locales/BreakpointListDialog_cs.properties @@ -0,0 +1,27 @@ +# Copyright (C) 2023 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 . + +dialog.title = Seznam breakpoint\u016f +button.goto = P\u0159ej\u00edt na +button.remove = Odebrat +button.removeAll = Odebrat v\u0161e +button.close = Zav\u0159\u00edt + +breakpoint.scriptName = N\u00e1zev skriptu +breakpoint.line = \u0158\u00e1dek +breakpoint.status = Stav +breakpoint.status.invalid = neplatn\u00fd +breakpoint.status.confirmed = potvrzen\u00fd +breakpoint.status.unknown = nezn\u00e1m\u00fd diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index a59cb3505..9542b1ac5 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -1248,4 +1248,6 @@ binarydata.dataInside.packer = It looks like this binary data is packed with %pa error.wrong.packer = %item%\r\nCannot unpack binary data with %packer%.\r\nThe data is probably not packed with this packer. contextmenu.copyTagToReplaceByClass = Copy tag to (replace by class name) -contextmenu.copyTagToReplaceByExportName = Copy tag to (replace by export name) \ No newline at end of file +contextmenu.copyTagToReplaceByExportName = Copy tag to (replace by export name) + +button.breakpointList = Show breakpoint list \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties index 300f2c46b..b9bfa3d08 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties @@ -1223,4 +1223,6 @@ binarydata.dataInside.packer = Vypad\u00e1 to, \u017ee tato bin\u00e1rn\u00ed da error.wrong.packer = %item%\r\nNelze rozbalit bin\u00e1rn\u00ed data pomoc\u00ed %packer%.\r\nTato data pravd\u011bpodobn\u011b nebyla zabalena t\u00edmto packerem. contextmenu.copyTagToReplaceByClass = Kop\u00edrovat tag do (nahradit podle n\u00e1zvu t\u0159\u00eddy) -contextmenu.copyTagToReplaceByExportName = Kop\u00edrovat tag do (nahradit podle exportovan\u00e9ho n\u00e1zvu) \ No newline at end of file +contextmenu.copyTagToReplaceByExportName = Kop\u00edrovat tag do (nahradit podle exportovan\u00e9ho n\u00e1zvu) + +button.breakpointList = Zobrazit seznam breakpoint\u016f \ No newline at end of file