From a738171637a2909f4fe07dabb6003fd403b09ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Mon, 15 Feb 2021 18:55:38 +0100 Subject: [PATCH] AS3 Add class --- CHANGELOG.md | 2 +- .../flash/gui/locales/MainFrame.properties | 4 +- .../flash/gui/locales/MainFrame_cs.properties | 4 +- .../flash/gui/tagtree/TagTreeContextMenu.java | 120 ++++++++++++++++-- 4 files changed, 119 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52e6f2652..8a91a918e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ All notable changes to this project will be documented in this file. - #1202 Check for modifications outside FFDec and ask user to reload - #1155, #1602 AS3 remove trait button - #1260, #1438 AS1/2 direct editing on(xxx), onClipEvent(xxx) handlers -- AS1/2 Add script (context menu on scripts folder) +- #1409, #1429, #1573, #1598 AS1/2/3 Add script/class (context menu on scripts folder) ### Fixed - #1298 AS1/2 properly decompiled setProperty/getProperty diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index 8e2bb7345..ef68a7542 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -783,4 +783,6 @@ button.removetrait = Remove trait message.confirm.removetrait = Do you really want to remove selected trait? -contextmenu.addScript = Add script \ No newline at end of file +contextmenu.addScript = Add script + +contextmenu.addClass = Add class \ 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 5660fc901..37444a5d4 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties @@ -761,4 +761,6 @@ button.removetrait = Odstranit vlastnost message.confirm.removetrait = Opravdu chcete odstranit vybranou vlastnost? -contextmenu.addScript = P\u0159idat skript \ No newline at end of file +contextmenu.addScript = P\u0159idat skript + +contextmenu.addClass = P\u0159idat t\u0159\u00eddu \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index 58bf12836..3594fe528 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -16,8 +16,12 @@ */ package com.jpexs.decompiler.flash.gui.tagtree; +import com.jpexs.decompiler.flash.IdentifiersDeobfuscation; import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScript3Parser; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.parser.ActionParseException; import com.jpexs.decompiler.flash.action.parser.script.ActionScript2Parser; @@ -26,11 +30,15 @@ import com.jpexs.decompiler.flash.gui.Main; import com.jpexs.decompiler.flash.gui.MainPanel; import com.jpexs.decompiler.flash.gui.ReplaceCharacterDialog; import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.abc.ClassesListTreeModel; import com.jpexs.decompiler.flash.gui.action.AddScriptDialog; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; import com.jpexs.decompiler.flash.tags.DefineButton2Tag; import com.jpexs.decompiler.flash.tags.DefineSoundTag; import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DoABC2Tag; +import com.jpexs.decompiler.flash.tags.DoABCTag; import com.jpexs.decompiler.flash.tags.DoActionTag; import com.jpexs.decompiler.flash.tags.DoInitActionTag; import com.jpexs.decompiler.flash.tags.ExportAssetsTag; @@ -50,6 +58,7 @@ import com.jpexs.decompiler.flash.timeline.Frame; import com.jpexs.decompiler.flash.timeline.FrameScript; import com.jpexs.decompiler.flash.timeline.TagScript; import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.AS3ClassTreeItem; import com.jpexs.decompiler.flash.treeitems.FolderItem; import com.jpexs.decompiler.flash.treeitems.SWFList; import com.jpexs.decompiler.flash.treeitems.TreeItem; @@ -58,6 +67,7 @@ import com.jpexs.decompiler.flash.types.CLIPACTIONRECORD; import com.jpexs.decompiler.flash.types.CLIPACTIONS; import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.Helper; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; @@ -131,13 +141,13 @@ public class TagTreeContextMenu extends JPopupMenu { private JMenuItem openSWFInsideTagMenuItem; - private JMenuItem addScriptMenuItem; + private JMenuItem addAs12ScriptMenuItem; + private JMenuItem addAs3ClassMenuItem; public TagTreeContextMenu(final TagTree tagTree, MainPanel mainPanel) { this.mainPanel = mainPanel; this.tagTree = tagTree; - expandRecursiveMenuItem = new JMenuItem(mainPanel.translate("contextmenu.expandAll")); expandRecursiveMenuItem.addActionListener(this::expandRecursiveActionPerformed); add(expandRecursiveMenuItem); @@ -214,9 +224,14 @@ public class TagTreeContextMenu extends JPopupMenu { add(openSWFInsideTagMenuItem); openSWFInsideTagMenuItem.addActionListener(this::openSwfInsideActionPerformed); - addScriptMenuItem = new JMenuItem(mainPanel.translate("contextmenu.addScript")); - addScriptMenuItem.addActionListener(this::addScriptActionPerformed); - add(addScriptMenuItem); + addAs12ScriptMenuItem = new JMenuItem(mainPanel.translate("contextmenu.addScript")); + addAs12ScriptMenuItem.addActionListener(this::addAs12ScriptActionPerformed); + add(addAs12ScriptMenuItem); + + addAs3ClassMenuItem = new JMenuItem(mainPanel.translate("contextmenu.addClass")); + addAs3ClassMenuItem.addActionListener(this::addAs3ClassActionPerformed); + add(addAs3ClassMenuItem); + tagTree.addMouseListener(new MouseAdapter() { @Override @@ -341,7 +356,8 @@ public class TagTreeContextMenu extends JPopupMenu { copyTagMenu.setVisible(false); copyTagWithDependenciesMenu.setVisible(false); openSWFInsideTagMenuItem.setVisible(false); - addScriptMenuItem.setVisible(false); + addAs12ScriptMenuItem.setVisible(false); + addAs3ClassMenuItem.setVisible(false); if (allSelectedIsTag) { boolean canUndo = false; @@ -391,9 +407,12 @@ public class TagTreeContextMenu extends JPopupMenu { if (firstItem instanceof FolderItem) { if (((FolderItem) firstItem).getName().equals(TagTreeModel.FOLDER_SCRIPTS)) { - addScriptMenuItem.setVisible(true); + addAs12ScriptMenuItem.setVisible(true); } } + if (firstItem instanceof ClassesListTreeModel) { + addAs3ClassMenuItem.setVisible(true); + } if (firstItem instanceof CharacterTag) { replaceWithTagMenuItem.setVisible(true); @@ -729,7 +748,92 @@ public class TagTreeContextMenu extends JPopupMenu { View.expandTreeNodes(tagTree, path, true); } - private void addScriptActionPerformed(ActionEvent evt) { + private void addAs3ClassActionPerformed(ActionEvent evt) { + List sel = tagTree.getSelected(); + if (!sel.isEmpty()) { + if (sel.get(0) instanceof ClassesListTreeModel) { + String className = View.showInputDialog(AppDialog.translateForDialog("classname", AddScriptDialog.class), ""); + if (className == null || className.isEmpty()) { + return; + } + ClassesListTreeModel cl = (ClassesListTreeModel) sel.get(0); + SWF swf = cl.getSwf(); + DoABC2Tag doAbc = new DoABC2Tag(swf); + doAbc.name = className; + + List abcs = new ArrayList<>(); + for (ABCContainerTag ct : swf.getAbcList()) { + abcs.add(ct.getABC()); + } + + ReadOnlyTagList tags = swf.getTags(); + int insertPos = -1; + for (int i = 0; i < tags.size(); i++) { + if (tags.get(i) instanceof ShowFrameTag) { + insertPos = i; + break; + } + } + if (insertPos == -1) { + insertPos = tags.size(); + } + swf.addTag(insertPos, doAbc); + + String pkg = className.contains(".") ? className.substring(0, className.lastIndexOf(".")) : ""; + String classSimpleName = className.contains(".") ? className.substring(className.lastIndexOf(".") + 1) : className; + String fileName = className.replace(".", "/"); + String pkgParts[] = new String[0]; + if (!pkg.isEmpty()) { + if (pkg.contains(".")) { + pkgParts = pkg.split("\\."); + } else { + pkgParts = new String[]{pkg}; + } + } + try { + ActionScript3Parser parser = new ActionScript3Parser(doAbc.getABC(), abcs); + + DottedChain dc = new DottedChain(pkgParts, ""); + String script = "package " + dc.toPrintableString(true) + " {" + + "public class " + IdentifiersDeobfuscation.printIdentifier(true, classSimpleName) + " {" + + " }" + + "}"; + parser.addScript(script, false, fileName, 0, 0); + } catch (IOException | InterruptedException | AVM2ParseException | CompilationException ex) { + Logger.getLogger(TagTreeContextMenu.class.getName()).log(Level.SEVERE, "Error during script compilation", ex); + } + + doAbc.setModified(true); + swf.clearAllCache(); + swf.setModified(true); + mainPanel.refreshTree(swf); + + Object item = mainPanel.tagTree.getModel().getScriptsNode(swf); + + String parts[] = className.contains(".") ? className.split("\\.") : new String[]{className}; + TreePath selection = mainPanel.tagTree.getSelectionPath(); + TreePath swfPath = selection.getParentPath(); + TreePath scriptsPath = swfPath.pathByAddingChild(item); + TreePath classPath = scriptsPath; + + for (int i = 0; i < parts.length; i++) { + for (TreeItem ti : tagTree.getModel().getAllChildren(item)) { + if (ti instanceof AS3ClassTreeItem) { + AS3ClassTreeItem cti = (AS3ClassTreeItem) ti; + if (parts[i].equals(cti.getNameWithNamespaceSuffix())) { + classPath = classPath.pathByAddingChild(ti); + item = ti; + break; + } + } + } + } + mainPanel.tagTree.setSelectionPath(classPath); + } + } + } + + private void addAs12ScriptActionPerformed(ActionEvent evt) { List sel = tagTree.getSelected(); if (!sel.isEmpty()) { if (sel.get(0) instanceof FolderItem) {