From b2ce046d0b453209f21fd959ecf2dc0bc97af9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Mon, 15 Feb 2021 07:34:23 +0100 Subject: [PATCH] AS2 add class --- .../decompiler/flash/timeline/AS2Package.java | 8 +- .../flash/gui/action/AddScriptDialog.java | 160 +++++++++++++----- .../locales/action/AddScriptDialog.properties | 2 + .../flash/gui/tagtree/TagTreeContextMenu.java | 109 ++++++++++++ 4 files changed, 234 insertions(+), 45 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/AS2Package.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/AS2Package.java index 5da6e5ad0..259c1b7ff 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/AS2Package.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/AS2Package.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.timeline; import com.jpexs.decompiler.flash.SWF; @@ -113,6 +114,11 @@ public class AS2Package implements TreeItem { return name; } + public String getName() { + return name; + } + + @Override public boolean isModified() { for (ASMSource s : scripts.values()) { diff --git a/src/com/jpexs/decompiler/flash/gui/action/AddScriptDialog.java b/src/com/jpexs/decompiler/flash/gui/action/AddScriptDialog.java index a74c0125a..f4bd45e8b 100644 --- a/src/com/jpexs/decompiler/flash/gui/action/AddScriptDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/action/AddScriptDialog.java @@ -16,6 +16,7 @@ */ package com.jpexs.decompiler.flash.gui.action; +import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.gui.AppDialog; import com.jpexs.decompiler.flash.gui.Main; @@ -25,6 +26,8 @@ import com.jpexs.decompiler.flash.gui.View; import com.jpexs.decompiler.flash.tags.DefineButton2Tag; import com.jpexs.decompiler.flash.tags.DefineSpriteTag; import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.ExportAssetsTag; +import com.jpexs.decompiler.flash.tags.ImportAssetsTag; import com.jpexs.decompiler.flash.tags.ShowFrameTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.ButtonTag; @@ -44,6 +47,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; +import javax.swing.Box; +import javax.swing.BoxLayout; import javax.swing.DefaultListCellRenderer; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -60,9 +65,7 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; import javax.swing.plaf.basic.BasicLabelUI; -import javax.swing.plaf.basic.BasicTreeUI; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; @@ -84,6 +87,8 @@ public class AddScriptDialog extends AppDialog { private PreviewPanel buttonPreviewPanel; private PreviewPanel instancePreviewPanel; + private JTextField classNameTextField; + private JTree instanceTree; private JTree spriteTree; @@ -102,15 +107,33 @@ public class AddScriptDialog extends AppDialog { public static final int TYPE_SPRITE_FRAME = 1; public static final int TYPE_BUTTON_EVENT = 2; public static final int TYPE_INSTANCE_EVENT = 3; + public static final int TYPE_CLASS = 4; + private final SWF swf; private final JComboBox typeComboBox; + private List existingClasses = new ArrayList<>(); + public AddScriptDialog(SWF swf) { setDefaultCloseOperation(HIDE_ON_CLOSE); setTitle(translate("dialog.title")); this.swf = swf; + + ReadOnlyTagList tags = swf.getTags(); + final String PACKAGES = "__Packages."; + for (Tag t : tags) { + if (t instanceof ExportAssetsTag) { + ExportAssetsTag ea = (ExportAssetsTag) t; + for (String n : ea.names) { + if (n.startsWith(PACKAGES)) { + existingClasses.add(n.substring(PACKAGES.length())); + } + } + } + } + Container cnt = getContentPane(); cnt.setLayout(new BorderLayout()); @@ -127,7 +150,8 @@ public class AddScriptDialog extends AppDialog { translate("type.frame"), translate("type.sprite.frame"), translate("type.button.event"), - translate("type.instance.event") + translate("type.instance.event"), + translate("type.class") }); typeComboBox.addActionListener(this::typeChangedActionPerformed); typeLabel.setLabelFor(typeComboBox); @@ -162,6 +186,7 @@ public class AddScriptDialog extends AppDialog { centerPanel.add(createSpritePanel(checkEnabledDocumentListener), "" + TYPE_SPRITE_FRAME); centerPanel.add(createButtonPanel(), "" + TYPE_BUTTON_EVENT); centerPanel.add(createInstancePanel(), "" + TYPE_INSTANCE_EVENT); + centerPanel.add(createClassPanel(), "" + TYPE_CLASS); cnt.add(centerPanel, BorderLayout.CENTER); cnt.add(panButtons, BorderLayout.SOUTH); @@ -247,7 +272,7 @@ public class AddScriptDialog extends AppDialog { sprite.setParent(root); sprite.setData(t); DefineSpriteTag s = (DefineSpriteTag) t; - int frame = 1; + int f = 1; boolean hasScript = false; for (Tag t2 : s.getTags()) { if (t2 instanceof DoActionTag) { @@ -255,12 +280,12 @@ public class AddScriptDialog extends AppDialog { } if (t2 instanceof ShowFrameTag) { MyTreeNode frameNode = new MyTreeNode(); - MyFrame myf = new MyFrame(frame); + MyFrame myf = new MyFrame(f); myf.setInvalid(hasScript); frameNode.setData(myf); frameNode.setParent(sprite); sprite.addChild(frameNode); - frame++; + f++; hasScript = false; } } @@ -285,12 +310,6 @@ public class AddScriptDialog extends AppDialog { }); spriteFrameTextField.getDocument().addDocumentListener(documentListener); - List sprites = new ArrayList<>(); - for (Tag t : swf.getTags()) { - if (t instanceof DefineSpriteTag) { - sprites.add((DefineSpriteTag) t); - } - } MyTreeNode root = new MyTreeNode(); root.setData("root"); @@ -361,10 +380,10 @@ public class AddScriptDialog extends AppDialog { } private JPanel createButtonPanel() { - List buttons = new ArrayList<>(); + List buttons = new ArrayList<>(); for (Tag t : swf.getTags()) { - if (t instanceof ButtonTag) { - buttons.add((ButtonTag) t); + if (t instanceof DefineButton2Tag) { + buttons.add((DefineButton2Tag) t); } } buttonList = new JList<>(buttons.toArray(new DefineButton2Tag[buttons.size()])); @@ -399,7 +418,7 @@ public class AddScriptDialog extends AppDialog { private static class MyTreeNode implements TreeNode { - private List children = new ArrayList<>(); + private final List children = new ArrayList<>(); private TreeNode parent; private Object data; @@ -463,7 +482,7 @@ public class AddScriptDialog extends AppDialog { private static class MyFrame { - private int frame; + private final int frame; private boolean invalid; public MyFrame(int frame) { @@ -489,7 +508,7 @@ public class AddScriptDialog extends AppDialog { } private void populateInstanceNodes(MyTreeNode root, Timelined tim) { - int frame = 1; + int f = 1; List currentFramePlaces = new ArrayList<>(); for (Tag t : tim.getTags()) { if (t instanceof DefineSpriteTag) { @@ -506,7 +525,7 @@ public class AddScriptDialog extends AppDialog { if (t instanceof ShowFrameTag) { if (!currentFramePlaces.isEmpty()) { MyTreeNode frameNode = new MyTreeNode(); - frameNode.setData(new MyFrame(frame)); + frameNode.setData(new MyFrame(f)); frameNode.setParent(root); for (MyTreeNode p : currentFramePlaces) { p.setParent(frameNode); @@ -515,7 +534,7 @@ public class AddScriptDialog extends AppDialog { root.addChild(frameNode); currentFramePlaces.clear(); } - frame++; + f++; } if (t instanceof PlaceObjectTypeTag) { MyTreeNode place = new MyTreeNode(); @@ -583,6 +602,44 @@ public class AddScriptDialog extends AppDialog { return instancePanel; } + private JPanel createClassPanel() { + JPanel classPanel = new JPanel(); + classPanel.setLayout(new BoxLayout(classPanel, BoxLayout.Y_AXIS)); + + JPanel classCenterPanel = new JPanel(new FlowLayout()); + JLabel classNameLabel = new JLabel(translate("classname")); + classNameTextField = new JTextField(40); + + classNameTextField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + checkEnabled(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + checkEnabled(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + checkEnabled(); + } + }); + + classNameTextField.addActionListener(this::okButtonActionPerformed); + + classNameLabel.setLabelFor(classNameTextField); + classCenterPanel.add(classNameLabel); + classCenterPanel.add(classNameTextField); + + classPanel.add(Box.createVerticalGlue()); + classPanel.add(classCenterPanel); + classPanel.add(Box.createVerticalGlue()); + + return classPanel; + } + private void instanceValueChanged(TreeSelectionEvent e) { TreePath selection = instanceTree.getSelectionPath(); if (selection == null) { @@ -595,12 +652,12 @@ public class AddScriptDialog extends AppDialog { PlaceObjectTypeTag place = (PlaceObjectTypeTag) tnode.getData(); instancePreviewPanel.selectImageDepth(place.getDepth()); - int frame = ((MyFrame) ((MyTreeNode) tnode.getParent()).getData()).frame; + int f = ((MyFrame) ((MyTreeNode) tnode.getParent()).getData()).frame; Object parent = ((MyTreeNode) tnode.getParent().getParent()).getData(); if (parent instanceof DefineSpriteTag) { - instancePreviewPanel.showImagePanel((DefineSpriteTag) parent, swf, frame - 1); + instancePreviewPanel.showImagePanel((DefineSpriteTag) parent, swf, f - 1); } else { - instancePreviewPanel.showImagePanel(swf, swf, frame - 1); + instancePreviewPanel.showImagePanel(swf, swf, f - 1); } } else if (tnode.getData() instanceof DefineSpriteTag) { @@ -608,12 +665,12 @@ public class AddScriptDialog extends AppDialog { instancePreviewPanel.showImagePanel((DefineSpriteTag) tnode.getData(), swf, -1); } else if (tnode.getData() instanceof MyFrame) { instancePreviewPanel.selectImageDepth(-1); - int frame = ((MyFrame) tnode.getData()).frame; + int f = ((MyFrame) tnode.getData()).frame; Object parent = ((MyTreeNode) tnode.getParent()).getData(); if (parent instanceof DefineSpriteTag) { - instancePreviewPanel.showImagePanel((DefineSpriteTag) parent, swf, frame - 1); + instancePreviewPanel.showImagePanel((DefineSpriteTag) parent, swf, f - 1); } else { - instancePreviewPanel.showImagePanel(swf, swf, frame - 1); + instancePreviewPanel.showImagePanel(swf, swf, f - 1); } } checkEnabled(); @@ -628,10 +685,10 @@ public class AddScriptDialog extends AppDialog { return; } framePreviewPanel.showImagePanel(swf, swf, selectedIndex); - int frame = selectedIndex + 1; + int f = selectedIndex + 1; - if (!frameTextField.getText().equals("" + frame)) { - frameTextField.setText("" + frame); + if (!frameTextField.getText().equals("" + f)) { + frameTextField.setText("" + f); } checkEnabled(); } @@ -657,15 +714,15 @@ public class AddScriptDialog extends AppDialog { if (tnode.getData() instanceof DefineSpriteTag) { spritePreviewPanel.showImagePanel((DefineSpriteTag) tnode.getData(), swf, -1); } else if (tnode.getData() instanceof MyFrame) { - int frame = ((MyFrame) tnode.getData()).frame; + int f = ((MyFrame) tnode.getData()).frame; Object parent = ((MyTreeNode) tnode.getParent()).getData(); if (parent instanceof DefineSpriteTag) { - spritePreviewPanel.showImagePanel((DefineSpriteTag) parent, swf, frame - 1); + spritePreviewPanel.showImagePanel((DefineSpriteTag) parent, swf, f - 1); } else { - spritePreviewPanel.showImagePanel(swf, swf, frame - 1); + spritePreviewPanel.showImagePanel(swf, swf, f - 1); } - if (!spriteFrameTextField.getText().equals("" + frame)) { - spriteFrameTextField.setText("" + frame); + if (!spriteFrameTextField.getText().equals("" + f)) { + spriteFrameTextField.setText("" + f); } } checkEnabled(); @@ -680,11 +737,11 @@ public class AddScriptDialog extends AppDialog { private void updateFrames() { int type = typeComboBox.getSelectedIndex(); if (type == TYPE_FRAME) { - int frame = -1; + int f = -1; boolean invalid = false; try { - frame = Integer.parseInt(frameTextField.getText()); - if (frame <= 0) { + f = Integer.parseInt(frameTextField.getText()); + if (f <= 0) { invalid = true; } @@ -692,11 +749,11 @@ public class AddScriptDialog extends AppDialog { invalid = true; } if (!invalid) { - if (frame > frameList.getModel().getSize()) { + if (f > frameList.getModel().getSize()) { frameList.setSelectedIndices(new int[]{}); } else { - frameList.setSelectedIndex(frame - 1); - frameList.ensureIndexIsVisible(frame - 1); + frameList.setSelectedIndex(f - 1); + frameList.ensureIndexIsVisible(f - 1); } } } @@ -704,11 +761,11 @@ public class AddScriptDialog extends AppDialog { TreePath selection = spriteTree.getSelectionPath(); if (selection != null) { - int frame = -1; + int f = -1; boolean invalid = false; try { - frame = Integer.parseInt(spriteFrameTextField.getText()); - if (frame <= 0) { + f = Integer.parseInt(spriteFrameTextField.getText()); + if (f <= 0) { invalid = true; } @@ -728,7 +785,7 @@ public class AddScriptDialog extends AppDialog { if (spriteTree.isExpanded(spritePath)) { boolean found = false; for (int i = 0; i < node.getChildCount(); i++) { - if (((MyFrame) ((MyTreeNode) node.getChildAt(i)).data).frame == frame) { + if (((MyFrame) ((MyTreeNode) node.getChildAt(i)).data).frame == f) { TreePath framePath = spritePath.pathByAddingChild(node.getChildAt(i)); spriteTree.setSelectionPath(framePath); found = true; @@ -814,6 +871,14 @@ public class AddScriptDialog extends AppDialog { } } + if (type == TYPE_CLASS) { + if (classNameTextField.getText().trim().isEmpty()) { + okButton.setEnabled(false); + } + if (existingClasses.contains(classNameTextField.getText().trim())) { + okButton.setEnabled(false); + } + } } private void okButtonActionPerformed(ActionEvent evt) { @@ -835,6 +900,13 @@ public class AddScriptDialog extends AppDialog { return frame; } + public String getClassName() { + if (getScriptType() == TYPE_CLASS) { + return classNameTextField.getText().trim(); + } + return null; + } + public int getScriptType() { return typeComboBox.getSelectedIndex(); } diff --git a/src/com/jpexs/decompiler/flash/gui/locales/action/AddScriptDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/action/AddScriptDialog.properties index 0e0b396ac..4a780ce2d 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/action/AddScriptDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/action/AddScriptDialog.properties @@ -7,3 +7,5 @@ type.frame = Main timeline frame script type.sprite.frame = Sprite frame script type.button.event = Button event type.instance.event = Instance event +type.class = Class +classname = Fully qualified class name: diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index 4785a6dc7..aa052c06f 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -18,6 +18,9 @@ package com.jpexs.decompiler.flash.gui.tagtree; import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.parser.ActionParseException; +import com.jpexs.decompiler.flash.action.parser.script.ActionScript2Parser; import com.jpexs.decompiler.flash.gui.AppDialog; import com.jpexs.decompiler.flash.gui.Main; import com.jpexs.decompiler.flash.gui.MainPanel; @@ -30,6 +33,7 @@ import com.jpexs.decompiler.flash.tags.DefineSoundTag; import com.jpexs.decompiler.flash.tags.DefineSpriteTag; import com.jpexs.decompiler.flash.tags.DoActionTag; import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import com.jpexs.decompiler.flash.tags.ExportAssetsTag; import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; import com.jpexs.decompiler.flash.tags.PlaceObject3Tag; import com.jpexs.decompiler.flash.tags.PlaceObject4Tag; @@ -41,6 +45,7 @@ import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.ImageTag; import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.timeline.AS2Package; import com.jpexs.decompiler.flash.timeline.Frame; import com.jpexs.decompiler.flash.timeline.FrameScript; import com.jpexs.decompiler.flash.timeline.TagScript; @@ -52,6 +57,7 @@ import com.jpexs.decompiler.flash.types.BUTTONCONDACTION; 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.helpers.Helper; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; @@ -760,11 +766,17 @@ public class TagTreeContextMenu extends JPopupMenu { } } if (!frameFound) { + //inserting new frames for (; frame < targetFrame; frame++) { tim.addTag(new ShowFrameTag(swf)); } tim.addTag(doAction); tim.addTag(new ShowFrameTag(swf)); + if (tim instanceof DefineSpriteTag) { + ((DefineSpriteTag) tim).frameCount = targetFrame; + } else { + swf.frameCount = targetFrame; + } } TreePath selection = mainPanel.tagTree.getSelectionPath(); @@ -931,6 +943,103 @@ public class TagTreeContextMenu extends JPopupMenu { } } } + } else if (addScriptDialog.getScriptType() == AddScriptDialog.TYPE_CLASS) { + String className = addScriptDialog.getClassName(); + ReadOnlyTagList tags = swf.getTags(); + List exportedIds = new ArrayList<>(); + for (int i = 0; i < tags.size(); i++) { + if (tags.get(i) instanceof ExportAssetsTag) { + ExportAssetsTag ea = (ExportAssetsTag) tags.get(i); + exportedIds.addAll(ea.tags); + } + } + + int insertPos = -1; + for (int i = 0; i < tags.size(); i++) { + if (tags.get(i) instanceof DoInitActionTag) { + DoInitActionTag doinit = (DoInitActionTag) tags.get(i); + if (!exportedIds.contains(doinit.spriteId)) { + //this is #initpragma, make sure class is inserted before it + insertPos = i; + break; + } + } + } + if (insertPos == -1) { + for (int i = 0; i < tags.size(); i++) { + if (tags.get(i) instanceof ShowFrameTag) { + insertPos = i; + break; + } + } + } + + if (insertPos > -1) { + int characterId = swf.getNextCharacterId(); + DefineSpriteTag sprite = new DefineSpriteTag(swf); + sprite.spriteId = characterId; + sprite.hasEndTag = true; + + String exportName = "__Packages." + className; + + ExportAssetsTag exportAssets = new ExportAssetsTag(swf); + exportAssets.names = new ArrayList<>(); + exportAssets.names.add(exportName); + exportAssets.tags = new ArrayList<>(); + exportAssets.tags.add(characterId); + + DoInitActionTag doInit = new DoInitActionTag(swf); + doInit.spriteId = characterId; + + ActionScript2Parser parser = new ActionScript2Parser(swf, doInit); + try { + List actions = parser.actionsFromString("class " + className + "{}"); + doInit.setActions(actions); + } catch (ActionParseException | IOException | CompilationException ex) { + //ignore + } + + sprite.setExportName(exportName); + + swf.addTag(insertPos, sprite); + swf.addTag(insertPos + 1, exportAssets); + swf.addTag(insertPos + 2, doInit); + + swf.clearAllCache(); + swf.setModified(true); + mainPanel.refreshTree(swf); + + TreePath selection = mainPanel.tagTree.getSelectionPath(); + TreePath swfPath = selection.getParentPath(); + FolderItem scriptsNode = (FolderItem) mainPanel.tagTree.getModel().getScriptsNode(swf); + TreePath scriptsPath = swfPath.pathByAddingChild(scriptsNode); + String classParts[] = className.contains(".") ? className.split("\\.") : new String[]{className}; + + for (TreeItem subItem : scriptsNode.subItems) { + if (subItem instanceof AS2Package) { + AS2Package pkg = (AS2Package) subItem; + if (pkg.getName().equals("__Packages")) { + TreePath classPath = scriptsPath.pathByAddingChild(pkg); + for (int i = 0; i < classParts.length - 1; i++) { + List subs = pkg.getAllChildren(); + for (TreeItem s : subs) { + if (s instanceof AS2Package) { + if (((AS2Package) s).getName().equals(classParts[i])) { + pkg = (AS2Package) s; + classPath = classPath.pathByAddingChild(pkg); + break; + } + } + } + } + classPath = classPath.pathByAddingChild(doInit); + mainPanel.tagTree.setSelectionPath(classPath); + break; + } + } + } + + } } } }