From 203ac6dcc9e7db74ffd7b764f8cba0f4cfc1fa1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Mon, 16 Feb 2026 18:19:23 +0100 Subject: [PATCH] Changed: Prepared files have _debug/_debugpcode suffix. Added: Prepare for p-code debugging. Added: Preparation progress Fixed: Nullpointer in tag when character not found. --- .../com/jpexs/decompiler/flash/tags/Tag.java | 3 + .../flash/gui/locales/MainFrame.properties | 6 +- .../flash/gui/locales/MainFrame_cs.properties | 6 +- .../flash/gui/tagtree/TagTreeContextMenu.java | 159 ++++++++++++++---- 4 files changed, 136 insertions(+), 38 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java index 458cf0ebe..32e5610f6 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java @@ -917,6 +917,9 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable { if (swf.getCharacters(true).containsKey(characterId) && !swf.getCyclicCharacters().contains(characterId)) { Set needed4 = new LinkedHashSet<>(); CharacterTag character = swf.getCharacter(characterId); + if (character == null) { + continue; + } if (character.isImported()) { continue; } diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index 35af43779..ba4d55ec1 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -1111,8 +1111,12 @@ work.generating.swc = Generating Swc... menu.file.start.debuglisten = Debug listen... work.debugging.listening = Listening for incoming connections... contextmenu.prepareDebug.injectDebug = Prepare file for debugging (+ inject debug info) +contextmenu.prepareDebug.injectDebug.pcode = Prepare file for P-code debugging (+ inject debug info) contextmenu.prepareDebug.generateSwd = Prepare file for debugging (+ generate SWD) -prepareDebug.title = Hit Save to overwrite current file or select another file. +contextmenu.prepareDebug.generateSwd.pcode = Prepare file for P-code debugging (+ generate SWD) +prepareDebug.title = Save prepared version +work.prepareDebug = Preparing file for debugging... +prepareDebug.finishedin = Prepared in %time% work.halted.with = Debugging of %file% started, execution halted. Add breakpoints and click Continue (F5) to resume running. debug.session = Session %id% debug.session.running = (Running) 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 0ceaf938e..1c0a73683 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties @@ -1110,8 +1110,12 @@ work.generating.swc = Generov\u00e1n\u00ed Swc... menu.file.start.debuglisten = Naslouchat lad\u011bn\u00ed... work.debugging.listening = Naslouch\u00e1m pro p\u0159\u00edchoz\u00ed spojen\u00ed... contextmenu.prepareDebug.injectDebug = P\u0159ipravit soubor pro lad\u011bn\u00ed (+ injektovat lad\u00edc\u00ed informace) +contextmenu.prepareDebug.injectDebug.pcode = P\u0159ipravit soubor pro lad\u011bn\u00ed P-k\u00f3du (+ injektovat lad\u00edc\u00ed informace) contextmenu.prepareDebug.generateSwd = P\u0159ipravit soubor pro lad\u011bn\u00ed (+ vytvo\u0159it SWD) -prepareDebug.title = Stiskni Ulo\u017eit pro p\u0159eps\u00e1n\u00ed nyn\u011bj\u0161\u00edhio souboru nebo vyber soubor jin\u00fd. +contextmenu.prepareDebug.generateSwd.pcode = P\u0159ipravit soubor pro lad\u011bn\u00ed P-k\u00f3du (+ vytvo\u0159it SWD) +prepareDebug.title = Ulo\u017een\u00ed p\u0159ipraven\u00e9 verze +work.prepareDebug = P\u0159ipravuji soubor pro lad\u011bn\u00ed... +prepareDebug.finishedin = P\u0159ipraveno za %time% work.halted.with = Lad\u011bn\u00ed %file% za\u010dalo, b\u011bh je nyn\u00ed pozastaven. P\u0159idejte breakpointy a klikn\u011bte na Pokra\u010dovat (F5) pro obnoven\u00ed b\u011bhu. debug.session = Sezen\u00ed %id% debug.session.running = (B\u011b\u017e\u00ed) diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index b6e7cdd3b..2c24044b6 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -150,6 +150,7 @@ import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.decompiler.graph.CompilationException; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; import com.jpexs.helpers.LinkedIdentityHashSet; import com.jpexs.helpers.Reference; @@ -251,7 +252,7 @@ public class TagTreeContextMenu extends JPopupMenu { private JMenuItem exportSwfXmlMenuItem; private JMenuItem saveSwcMenuItem; - + private JMenuItem saveExeMenuItem; private JMenuItem importSwfXmlMenuItem; @@ -379,13 +380,17 @@ public class TagTreeContextMenu extends JPopupMenu { private JMenuItem convertShapeTypeMenuItem; private JMenuItem convertPlaceObjectTypeMenuItem; - + private JMenuItem normalizeFontsMenuItem; - + private JMenuItem prepareDebugInject; - + private JMenuItem prepareDebugSwd; - + + private JMenuItem prepareDebugInjectPCode; + + private JMenuItem prepareDebugSwdPCode; + private List items = new ArrayList<>(); private static final int KIND_TAG_MOVETO = 0; @@ -576,17 +581,16 @@ public class TagTreeContextMenu extends JPopupMenu { exportSwfXmlMenuItem.setIcon(View.getIcon("exportxml16")); add(exportSwfXmlMenuItem); - saveSwcMenuItem = new JMenuItem(mainPanel.translate("contextmenu.saveSwc")); saveSwcMenuItem.addActionListener(this::saveSwcActionPerformed); saveSwcMenuItem.setIcon(View.getIcon("bundleswc16")); add(saveSwcMenuItem); - + saveExeMenuItem = new JMenuItem(mainPanel.translate("menu.file.saveasexe")); saveExeMenuItem.addActionListener(this::saveExeActionPerformed); saveExeMenuItem.setIcon(View.getIcon("saveasexe16")); add(saveExeMenuItem); - + addSeparator(); rawEditMenuItem = new JMenuItem(mainPanel.translate("contextmenu.rawEdit")); @@ -649,25 +653,33 @@ public class TagTreeContextMenu extends JPopupMenu { convertPlaceObjectTypeMenuItem.setIcon(View.getIcon("placeobject16")); add(convertPlaceObjectTypeMenuItem); - normalizeFontsMenuItem = new JMenuItem(mainPanel.translate("contextmenu.normalizeFonts")); normalizeFontsMenuItem.addActionListener(this::normalizeFontsActionPerformed); normalizeFontsMenuItem.setIcon(View.getIcon("font16")); add(normalizeFontsMenuItem); - + addSeparator(); prepareDebugInject = new JMenuItem(mainPanel.translate("contextmenu.prepareDebug.injectDebug")); prepareDebugInject.addActionListener(this::prepareDebugActionPerformed); prepareDebugInject.setIcon(View.getIcon("debug16")); add(prepareDebugInject); - + + prepareDebugInjectPCode = new JMenuItem(mainPanel.translate("contextmenu.prepareDebug.injectDebug.pcode")); + prepareDebugInjectPCode.addActionListener(this::prepareDebugPCodeActionPerformed); + prepareDebugInjectPCode.setIcon(View.getIcon("debug16")); + add(prepareDebugInjectPCode); + prepareDebugSwd = new JMenuItem(mainPanel.translate("contextmenu.prepareDebug.generateSwd")); prepareDebugSwd.addActionListener(this::prepareDebugActionPerformed); prepareDebugSwd.setIcon(View.getIcon("debug16")); add(prepareDebugSwd); - + prepareDebugSwdPCode = new JMenuItem(mainPanel.translate("contextmenu.prepareDebug.generateSwd.pcode")); + prepareDebugSwdPCode.addActionListener(this::prepareDebugPCodeActionPerformed); + prepareDebugSwdPCode.setIcon(View.getIcon("debug16")); + add(prepareDebugSwdPCode); + gotoDocumentClassMenuItem = new JMenuItem(mainPanel.translate("menu.tools.gotoDocumentClass")); gotoDocumentClassMenuItem.addActionListener(this::gotoDocumentClassActionPerformed); gotoDocumentClassMenuItem.setIcon(View.getIcon("gotomainclass16")); @@ -1370,7 +1382,9 @@ public class TagTreeContextMenu extends JPopupMenu { boolean hasExportableNodes = tree.hasExportableNodes(); prepareDebugInject.setVisible(false); + prepareDebugInjectPCode.setVisible(false); prepareDebugSwd.setVisible(false); + prepareDebugSwdPCode.setVisible(false); gotoDocumentClassMenuItem.setVisible(false); setAsLinkageMenuItem.setVisible(false); setAs3ClassLinkageMenuItem.setVisible(false); @@ -1566,7 +1580,7 @@ public class TagTreeContextMenu extends JPopupMenu { if ("__Packages".equals(firstPkg)) { addAs12ClassMenuItem.setVisible(true); } - } + } if (firstItem instanceof ClassesListTreeModel) { addAs3ClassMenuItem.setVisible(true); if (firstItem.getOpenable() instanceof SWF) { @@ -1604,8 +1618,10 @@ public class TagTreeContextMenu extends JPopupMenu { if (((SWF) firstItem).isAS3()) { gotoDocumentClassMenuItem.setVisible(true); prepareDebugInject.setVisible(true); + prepareDebugInjectPCode.setVisible(true); } else { prepareDebugSwd.setVisible(true); + prepareDebugSwdPCode.setVisible(true); } } @@ -2918,17 +2934,15 @@ public class TagTreeContextMenu extends JPopupMenu { mainPanel.setTagTreeSelectedNode(mainPanel.getCurrentTree(), lastConverted); } } - + private void normalizeFontsActionPerformed(ActionEvent evt) { for (TreeItem item : getSelectedItems()) { SWF swf = (SWF) item; FontNormalizer normalizer = new FontNormalizer(); - normalizer.normalizeFonts(swf); + normalizer.normalizeFonts(swf); } mainPanel.getCurrentTree().repaint(); } - - private void replaceRefsWithTagActionPerformed(ActionEvent evt) { TreeItem itemr = getCurrentItem(); @@ -3051,9 +3065,9 @@ public class TagTreeContextMenu extends JPopupMenu { } Main.getMainFrame().getPanel().refreshTree(); } - + private void saveSwcActionPerformed(ActionEvent evt) { - SWF swf = (SWF) getCurrentItem(); + SWF swf = (SWF) getCurrentItem(); Main.saveSwc(swf); } @@ -3077,12 +3091,20 @@ public class TagTreeContextMenu extends JPopupMenu { private void prepareDebugActionPerformed(ActionEvent evt) { TreeItem item = getCurrentItem(); - SWF swf = (SWF) item.getOpenable(); - JFileChooser chooser = View.getFileChooserWithIcon("debug"); + SWF swf = (SWF) item.getOpenable(); + JFileChooser chooser = View.getFileChooserWithIcon("debug"); if (swf.getFile() != null) { File dir = new File(swf.getFile()).getParentFile(); chooser.setCurrentDirectory(dir); - chooser.setSelectedFile(new File(swf.getFile())); + File file = new File(swf.getFile()); + //add _debug extension + String name = file.getName(); + if (name.contains(".")) { + name = name.substring(0, name.lastIndexOf(".")) + "_debug" + name.substring(name.lastIndexOf(".")); + } else { + name = name + "_debug"; + } + chooser.setSelectedFile(new File(dir, name)); chooser.setDialogTitle(AppStrings.translate("prepareDebug.title")); } chooser.setFileFilter(new FileFilter() { @@ -3100,17 +3122,82 @@ public class TagTreeContextMenu extends JPopupMenu { return; } File selFile = Helper.fixDialogFile(chooser.getSelectedFile()); - - boolean doPCode = false; - List tempFiles = new ArrayList<>(); - try { - Main.prepareForDebug(swf, selFile, selFile.getParentFile(), tempFiles, doPCode); - } catch (IOException | InterruptedException ex) { - ViewMessages.showMessageDialog(mainPanel, ex.toString(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - Main.stopWork(); + prepareDebugGeneral(false, swf, selFile); } - + + private void prepareDebugPCodeActionPerformed(ActionEvent evt) { + TreeItem item = getCurrentItem(); + SWF swf = (SWF) item.getOpenable(); + JFileChooser chooser = View.getFileChooserWithIcon("debug"); + if (swf.getFile() != null) { + File dir = new File(swf.getFile()).getParentFile(); + chooser.setCurrentDirectory(dir); + File file = new File(swf.getFile()); + //add _debugpcode extension + String name = file.getName(); + if (name.contains(".")) { + name = name.substring(0, name.lastIndexOf(".")) + "_debugpcode" + name.substring(name.lastIndexOf(".")); + } else { + name = name + "_debugpcode"; + } + chooser.setSelectedFile(new File(dir, name)); + chooser.setDialogTitle(AppStrings.translate("prepareDebug.title")); + } + chooser.setFileFilter(new FileFilter() { + @Override + public boolean accept(File f) { + return f.getAbsolutePath().toLowerCase().endsWith(".swf") || f.isDirectory(); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.swf"); + } + }); + if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) { + return; + } + File selFile = Helper.fixDialogFile(chooser.getSelectedFile()); + + prepareDebugGeneral(true, swf, selFile); + } + + private void prepareDebugGeneral(boolean pCode, SWF swf, File selFile) { + long timeBefore = System.currentTimeMillis(); + CancellableWorker prepareDebugWorker = new CancellableWorker("prepareDebugWorker") { + @Override + protected Object doInBackground() throws Exception { + List tempFiles = new ArrayList<>(); + + try { + Main.prepareForDebug(swf, selFile, selFile.getParentFile(), tempFiles, pCode); + } catch (IOException | InterruptedException ex) { + ViewMessages.showMessageDialog(mainPanel, ex.toString(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + + return null; + } + + @Override + protected void done() { + Main.stopWork(); + long timeAfter = System.currentTimeMillis(); + final long timeMs = timeAfter - timeBefore; + + View.execInEventDispatch(() -> { + mainPanel.setStatus(AppStrings.translate("prepareDebug.finishedin").replace("%time%", Helper.formatTimeSec(timeMs))); + }); + } + + @Override + public void workerCancelled() { + Main.stopWork(); + } + }; + Main.startWork(AppStrings.translate("work.prepareDebug"), prepareDebugWorker); + prepareDebugWorker.execute(); + } + private void gotoDocumentClassActionPerformed(ActionEvent evt) { TreeItem item = getCurrentItem(); item.getOpenable(); @@ -4025,9 +4112,9 @@ public class TagTreeContextMenu extends JPopupMenu { } private void createAs2Class(String className, SWF swf) { - + className = DottedChain.parsePrintable(swf, className).toRawString(); - + ReadOnlyTagList tags = swf.getTags(); List exportedIds = new ArrayList<>(); for (int i = 0; i < tags.size(); i++) { @@ -4082,7 +4169,7 @@ public class TagTreeContextMenu extends JPopupMenu { String[] parts = className.contains(".") ? className.split("\\.") : new String[]{className}; DottedChain dc = new DottedChain(parts); - try { + try { Set used = new LinkedHashSet<>(); String sourceCode = "class " + dc.toPrintableString(used, swf, false) + "{}"; StringBuilder sb = new StringBuilder(); @@ -5135,7 +5222,7 @@ public class TagTreeContextMenu extends JPopupMenu { neededClasses.add(cls); } } - + Set neededChars = Collections.newSetFromMap(new IdentityHashMap<>()); for (Integer characterId : needed) { CharacterTag neededTag = sourceSwf.getCharacter(characterId);