diff --git a/src/com/jpexs/decompiler/flash/gui/TagTree.java b/src/com/jpexs/decompiler/flash/gui/TagTree.java index e5687ff57..d0e99eea9 100644 --- a/src/com/jpexs/decompiler/flash/gui/TagTree.java +++ b/src/com/jpexs/decompiler/flash/gui/TagTree.java @@ -1,614 +1,614 @@ -/* - * Copyright (C) 2010-2014 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.ScriptPack; -import com.jpexs.decompiler.flash.gui.abc.treenodes.TreeElement; -import com.jpexs.decompiler.flash.gui.treenodes.SWFNode; -import com.jpexs.decompiler.flash.gui.treenodes.TagTreeRoot; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; -import com.jpexs.decompiler.flash.tags.DefineBitsTag; -import com.jpexs.decompiler.flash.tags.DefineButton2Tag; -import com.jpexs.decompiler.flash.tags.DefineButtonTag; -import com.jpexs.decompiler.flash.tags.DefineEditTextTag; -import com.jpexs.decompiler.flash.tags.DefineFont2Tag; -import com.jpexs.decompiler.flash.tags.DefineFont3Tag; -import com.jpexs.decompiler.flash.tags.DefineFont4Tag; -import com.jpexs.decompiler.flash.tags.DefineFontTag; -import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; -import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; -import com.jpexs.decompiler.flash.tags.DefineShape2Tag; -import com.jpexs.decompiler.flash.tags.DefineShape3Tag; -import com.jpexs.decompiler.flash.tags.DefineShape4Tag; -import com.jpexs.decompiler.flash.tags.DefineShapeTag; -import com.jpexs.decompiler.flash.tags.DefineSoundTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DefineText2Tag; -import com.jpexs.decompiler.flash.tags.DefineTextTag; -import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.SoundStreamHead2Tag; -import com.jpexs.decompiler.flash.tags.SoundStreamHeadTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.treeitems.AS2PackageNodeItem; -import com.jpexs.decompiler.flash.treeitems.AS3PackageNodeItem; -import com.jpexs.decompiler.flash.treeitems.FrameNodeItem; -import com.jpexs.decompiler.flash.treeitems.SWFList; -import com.jpexs.decompiler.flash.treeitems.StringItem; -import com.jpexs.decompiler.flash.treeitems.TreeElementItem; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.flash.treenodes.ContainerNode; -import com.jpexs.decompiler.flash.treenodes.FrameNode; -import com.jpexs.decompiler.flash.treenodes.TreeNode; -import com.jpexs.helpers.Helper; -import java.awt.Color; -import java.awt.Component; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.List; -import javax.swing.JComponent; -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPopupMenu; -import javax.swing.JTree; -import javax.swing.SwingUtilities; -import javax.swing.plaf.basic.BasicLabelUI; -import javax.swing.plaf.basic.BasicTreeUI; -import javax.swing.tree.DefaultTreeCellRenderer; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; -import javax.swing.tree.TreeSelectionModel; - -/** - * - * @author JPEXS - */ -public class TagTree extends JTree implements ActionListener { - - private static final String ACTION_RAW_EDIT = "RAWEDIT"; - private static final String ACTION_JUMP_TO_CHARACTER = "JUMPTOCHARACTER"; - private static final String ACTION_REMOVE_ITEM = "REMOVEITEM"; - private static final String ACTION_REMOVE_ITEM_WITH_DEPENDENCIES = "REMOVEITEMWITHDEPENDENCIES"; - private static final String ACTION_CLOSE_SWF = "CLOSESWF"; - private static final String ACTION_EXPAND_RECURSIVE = "EXPANDRECURSIVE"; - - private final MainPanel mainPanel; - - public class TagTreeCellRenderer extends DefaultTreeCellRenderer { - - @Override - public Component getTreeCellRendererComponent( - JTree tree, - Object value, - boolean sel, - boolean expanded, - boolean leaf, - int row, - boolean hasFocus) { - - super.getTreeCellRendererComponent( - tree, value, sel, - expanded, leaf, row, - hasFocus); - TreeNode treeNode = (TreeNode) value; - TreeItem val = treeNode.getItem(); - TreeNodeType type = getTreeNodeType(val); - if (type != null) { - if (type == TreeNodeType.FOLDER && expanded) { - type = TreeNodeType.FOLDER_OPEN; - } - String itemName = type.toString(); - if (type == TreeNodeType.FOLDER || type == TreeNodeType.FOLDER_OPEN) { - if (val instanceof StringItem) { - StringItem si = (StringItem) val; - if (!TagTreeRoot.FOLDER_ROOT.equals(si.getName())) { - itemName = "folder" + si.getName(); - } - } - } - String tagTypeStr = itemName.toLowerCase().replace("_", ""); - setIcon(View.getIcon(tagTypeStr + "16")); - } - - Font font = getFont(); - boolean isModified = false; - if (treeNode instanceof TreeNode) { - if (treeNode.getItem() instanceof Tag) { - Tag tag = (Tag) treeNode.getItem(); - if (tag.isModified()) { - isModified = true; - } - } - } - - if (isModified) { - font = font.deriveFont(Font.BOLD); - } else { - font = font.deriveFont(Font.PLAIN); - } - setFont(font); - - setUI(new BasicLabelUI()); - setOpaque(false); - //setBackground(Color.green); - setBackgroundNonSelectionColor(Color.white); - //setBackgroundSelectionColor(Color.ORANGE); - - return this; - } - } - - TagTree(TagTreeModel treeModel, MainPanel mainPanel) { - super(treeModel); - this.mainPanel = mainPanel; - setCellRenderer(new TagTreeCellRenderer()); - setRootVisible(false); - setBackground(Color.white); - setUI(new BasicTreeUI() { - @Override - public void paint(Graphics g, JComponent c) { - setHashColor(Color.gray); - super.paint(g, c); - } - }); - } - - public static TreeNodeType getTreeNodeType(TreeItem t) { - if ((t instanceof DefineFontTag) - || (t instanceof DefineFont2Tag) - || (t instanceof DefineFont3Tag) - || (t instanceof DefineFont4Tag) - || (t instanceof DefineCompactedFont)) { - return TreeNodeType.FONT; - } - if ((t instanceof DefineTextTag) - || (t instanceof DefineText2Tag) - || (t instanceof DefineEditTextTag)) { - return TreeNodeType.TEXT; - } - - if ((t instanceof DefineBitsTag) - || (t instanceof DefineBitsJPEG2Tag) - || (t instanceof DefineBitsJPEG3Tag) - || (t instanceof DefineBitsJPEG4Tag) - || (t instanceof DefineBitsLosslessTag) - || (t instanceof DefineBitsLossless2Tag)) { - return TreeNodeType.IMAGE; - } - if ((t instanceof DefineShapeTag) - || (t instanceof DefineShape2Tag) - || (t instanceof DefineShape3Tag) - || (t instanceof DefineShape4Tag)) { - return TreeNodeType.SHAPE; - } - - if ((t instanceof DefineMorphShapeTag) || (t instanceof DefineMorphShape2Tag)) { - return TreeNodeType.MORPH_SHAPE; - } - - if (t instanceof DefineSpriteTag) { - return TreeNodeType.SPRITE; - } - if ((t instanceof DefineButtonTag) || (t instanceof DefineButton2Tag)) { - return TreeNodeType.BUTTON; - } - if (t instanceof ASMSource) { - return TreeNodeType.AS; - } - if (t instanceof ScriptPack) { - return TreeNodeType.AS; - } - if (t instanceof AS2PackageNodeItem) { - return TreeNodeType.PACKAGE; - } - if (t instanceof AS3PackageNodeItem) { - return TreeNodeType.PACKAGE; - } - if (t instanceof FrameNodeItem) { - return TreeNodeType.FRAME; - } - if (t instanceof ShowFrameTag) { - return TreeNodeType.SHOW_FRAME; - } - - if (t instanceof DefineVideoStreamTag) { - return TreeNodeType.MOVIE; - } - - if ((t instanceof DefineSoundTag) || (t instanceof SoundStreamHeadTag) || (t instanceof SoundStreamHead2Tag)) { - return TreeNodeType.SOUND; - } - - if (t instanceof DefineBinaryDataTag) { - return TreeNodeType.BINARY_DATA; - } - - if (t instanceof SWF) { - return TreeNodeType.FLASH; - } - - if (t instanceof SWFList) { - SWFList slist = (SWFList) t; - if (slist.name != null) { - if (slist.name.toLowerCase().endsWith(".zip")) { - return TreeNodeType.BUNDLE_ZIP; - } - if (slist.name.toLowerCase().endsWith(".swc")) { - return TreeNodeType.BUNDLE_SWC; - } else { - return TreeNodeType.BUNDLE_BINARY; - } - } - } - - if (t instanceof Tag) { - return TreeNodeType.OTHER_TAG; - } - - return TreeNodeType.FOLDER; - } - - public void createContextMenu(final List swfs) { - final JPopupMenu contextPopupMenu = new JPopupMenu(); - - final JMenuItem expandRecursiveMenuItem = new JMenuItem(mainPanel.translate("contextmenu.expandAll")); - expandRecursiveMenuItem.addActionListener(this); - expandRecursiveMenuItem.setActionCommand(ACTION_EXPAND_RECURSIVE); - contextPopupMenu.add(expandRecursiveMenuItem); - - final JMenuItem removeMenuItem = new JMenuItem(mainPanel.translate("contextmenu.remove")); - removeMenuItem.addActionListener(this); - removeMenuItem.setActionCommand(ACTION_REMOVE_ITEM); - contextPopupMenu.add(removeMenuItem); - - final JMenuItem removeWithDependenciesMenuItem = new JMenuItem(mainPanel.translate("contextmenu.removeWithDependencies")); - removeWithDependenciesMenuItem.addActionListener(this); - removeWithDependenciesMenuItem.setActionCommand(ACTION_REMOVE_ITEM_WITH_DEPENDENCIES); - contextPopupMenu.add(removeWithDependenciesMenuItem); - - final JMenuItem exportSelectionMenuItem = new JMenuItem(mainPanel.translate("menu.file.export.selection")); - exportSelectionMenuItem.setActionCommand(MainFrameRibbonMenu.ACTION_EXPORT_SEL); - exportSelectionMenuItem.addActionListener(this); - contextPopupMenu.add(exportSelectionMenuItem); - - final JMenuItem replaceSelectionMenuItem = new JMenuItem(mainPanel.translate("button.replace")); - replaceSelectionMenuItem.setActionCommand(MainPanel.ACTION_REPLACE); - replaceSelectionMenuItem.addActionListener(mainPanel); - contextPopupMenu.add(replaceSelectionMenuItem); - - final JMenuItem rawEditMenuItem = new JMenuItem(mainPanel.translate("contextmenu.rawEdit")); - rawEditMenuItem.setActionCommand(ACTION_RAW_EDIT); - rawEditMenuItem.addActionListener(this); - rawEditMenuItem.setVisible(false); - contextPopupMenu.add(rawEditMenuItem); - - final JMenuItem jumpToCharacterMenuItem = new JMenuItem(mainPanel.translate("contextmenu.jumpToCharacter")); - jumpToCharacterMenuItem.setActionCommand(ACTION_JUMP_TO_CHARACTER); - jumpToCharacterMenuItem.addActionListener(this); - jumpToCharacterMenuItem.setVisible(false); - contextPopupMenu.add(jumpToCharacterMenuItem); - - final JMenuItem closeSelectionMenuItem = new JMenuItem(mainPanel.translate("contextmenu.closeSwf")); - closeSelectionMenuItem.setActionCommand(ACTION_CLOSE_SWF); - closeSelectionMenuItem.addActionListener(this); - contextPopupMenu.add(closeSelectionMenuItem); - - final JMenu moveTagMenu = new JMenu(mainPanel.translate("contextmenu.moveTag")); - contextPopupMenu.add(moveTagMenu); - - addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (SwingUtilities.isRightMouseButton(e)) { - - int row = getClosestRowForLocation(e.getX(), e.getY()); - int[] selectionRows = getSelectionRows(); - if (!Helper.contains(selectionRows, row)) { - setSelectionRow(row); - } - - TreePath[] paths = getSelectionPaths(); - if (paths == null || paths.length == 0) { - return; - } - boolean allSelectedIsTagOrFrame = true; - for (TreePath treePath : paths) { - TreeNode treeNode = (TreeNode) treePath.getLastPathComponent(); - - TreeItem tag = treeNode.getItem(); - if (!(tag instanceof Tag) && !(tag instanceof FrameNodeItem)) { - allSelectedIsTagOrFrame = false; - break; - } - } - - replaceSelectionMenuItem.setVisible(false); - closeSelectionMenuItem.setVisible(false); - moveTagMenu.setVisible(false); - expandRecursiveMenuItem.setVisible(false); - - if (paths.length == 1) { - TreeNode treeNode = (TreeNode) paths[0].getLastPathComponent(); - - TreeItem item = ((TreeNode) treeNode).getItem(); - - if (item instanceof ImageTag && ((ImageTag) item).importSupported()) { - replaceSelectionMenuItem.setVisible(true); - } - - if (item instanceof DefineBinaryDataTag) { - replaceSelectionMenuItem.setVisible(true); - } - - if (item instanceof DefineSoundTag) { - replaceSelectionMenuItem.setVisible(true); - } - - if (treeNode instanceof SWFNode) { - closeSelectionMenuItem.setVisible(true); - } - - if (item instanceof Tag && swfs.size() > 1) { - final Tag tag = (Tag) item; - moveTagMenu.removeAll(); - for (SWFList targetSwfList : swfs) { - for (final SWF targetSwf : targetSwfList) { - if (targetSwf != tag.getSwf()) { - JMenuItem swfItem = new JMenuItem(targetSwf.getShortFileName()); - swfItem.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent ae) { - tag.getSwf().tags.remove(tag); - tag.setSwf(targetSwf); - targetSwf.tags.add(tag); - mainPanel.refreshTree(); - } - }); - moveTagMenu.add(swfItem); - } - } - } - moveTagMenu.setVisible(true); - } - - TreeModel model = getModel(); - expandRecursiveMenuItem.setVisible(model.getChildCount(treeNode) > 0); - - jumpToCharacterMenuItem.setVisible(item instanceof CharacterIdTag && !(item instanceof CharacterTag)); - - rawEditMenuItem.setVisible(item instanceof Tag); - } - - removeMenuItem.setVisible(allSelectedIsTagOrFrame); - exportSelectionMenuItem.setEnabled(hasExportableNodes()); - contextPopupMenu.show(e.getComponent(), e.getX(), e.getY()); - } - } - }); - } - - @Override - public void actionPerformed(ActionEvent e) { - switch (e.getActionCommand()) { - case ACTION_RAW_EDIT: { - TreeItem item = getCurrentTreeItem(); - if (item == null) { - return; - } - - mainPanel.showGenericTag((Tag) item); - } - break; - case ACTION_JUMP_TO_CHARACTER: { - TreeItem item = getCurrentTreeItem(); - if (item == null || !(item instanceof CharacterIdTag)) { - return; - } - - CharacterIdTag characterIdTag = (CharacterIdTag) item; - mainPanel.setTreeItem(item.getSwf().characters.get(characterIdTag.getCharacterId())); - } - break; - case ACTION_EXPAND_RECURSIVE: { - TreePath path = getSelectionPath(); - if (path == null) { - return; - } - View.expandTreeNodesRecursive(this, path, true); - } - break; - case ACTION_REMOVE_ITEM: - case ACTION_REMOVE_ITEM_WITH_DEPENDENCIES: - List sel = getSelected(this); - - List tagsToRemove = new ArrayList<>(); - for (TreeNode o : sel) { - TreeItem tag = o.getItem(); - if (tag instanceof Tag) { - tagsToRemove.add((Tag) tag); - } else if (tag instanceof FrameNodeItem) { - FrameNodeItem frameNode = (FrameNodeItem) tag; - Frame frame = frameNode.getParent().getTimeline().frames.get(frameNode.getFrame() - 1); - if (frame.showFrameTag != null) { - tagsToRemove.add(frame.showFrameTag); - } else { - // this should be the last frame, so remove the inner tags - tagsToRemove.addAll(frame.innerTags); - } - } - } - - boolean removeDependencies = e.getActionCommand().equals(ACTION_REMOVE_ITEM_WITH_DEPENDENCIES); - if (tagsToRemove.size() == 1) { - Tag tag = tagsToRemove.get(0); - if (View.showConfirmDialog(this, mainPanel.translate("message.confirm.remove").replace("%item%", tag.toString()), mainPanel.translate("message.confirm"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { - tag.getSwf().removeTag(tag, removeDependencies); - mainPanel.refreshTree(); - } - } else if (tagsToRemove.size() > 1) { - if (View.showConfirmDialog(this, mainPanel.translate("message.confirm.removemultiple").replace("%count%", Integer.toString(tagsToRemove.size())), mainPanel.translate("message.confirm"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { - for (Tag tag : tagsToRemove) { - tag.getSwf().removeTag(tag, removeDependencies); - } - mainPanel.refreshTree(); - } - } - break; - case ACTION_CLOSE_SWF: { - Main.closeFile(mainPanel.getCurrentSwfList()); - } - } - } - - public boolean hasExportableNodes() { - return !getSelection(mainPanel.getCurrentSwf()).isEmpty(); - } - - public List getAllSubs(JTree tree, TreeNode o) { - TagTreeModel tm = (TagTreeModel) tree.getModel(); - List ret = new ArrayList<>(); - for (int i = 0; i < tm.getChildCount(o); i++) { - TreeNode c = tm.getChild(o, i); - ret.add(c); - ret.addAll(getAllSubs(tree, c)); - } - return ret; - } - - public List getAllSelected(TagTree tree) { - TreeSelectionModel tsm = tree.getSelectionModel(); - TreePath[] tps = tsm.getSelectionPaths(); - List ret = new ArrayList<>(); - if (tps == null) { - return ret; - } - - for (TreePath tp : tps) { - TreeNode treeNode = (TreeNode) tp.getLastPathComponent(); - ret.add(treeNode); - ret.addAll(getAllSubs(tree, treeNode)); - } - return ret; - } - - public List getSelected(JTree tree) { - TreeSelectionModel tsm = tree.getSelectionModel(); - TreePath[] tps = tsm.getSelectionPaths(); - List ret = new ArrayList<>(); - if (tps == null) { - return ret; - } - - for (TreePath tp : tps) { - TreeNode treeNode = (TreeNode) tp.getLastPathComponent(); - ret.add(treeNode); - } - return ret; - } - - public List getSelection(SWF swf) { - List ret = new ArrayList<>(); - List sel = getAllSelected(this); - for (TreeNode d : sel) { - if (d.getItem().getSwf() != swf) { - continue; - } - if (d instanceof ContainerNode) { - ContainerNode n = (ContainerNode) d; - TreeNodeType nodeType = TagTree.getTreeNodeType(n.getItem()); - if (nodeType == TreeNodeType.IMAGE) { - ret.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.SHAPE) { - ret.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.MORPH_SHAPE) { - ret.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.AS) { - ret.add(n); - } - if (nodeType == TreeNodeType.MOVIE) { - ret.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.SOUND) { - ret.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.BINARY_DATA) { - ret.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.TEXT) { - ret.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.FONT) { - ret.add((Tag) n.getItem()); - } - } - if (d instanceof FrameNode) { - FrameNode fn = (FrameNode) d; - if (!fn.scriptsNode) { - ret.add(d.getItem()); - } - } - if (d instanceof TreeElement) { - if (((TreeElement) d).isLeaf()) { - TreeElement treeElement = (TreeElement) d; - ret.add((ScriptPack) treeElement.getItem()); - } - } - } - return ret; - } - - public List getTagsWithType(List list, TreeNodeType type) { - List ret = new ArrayList<>(); - for (TreeElementItem item : list) { - TreeNodeType ttype = getTreeNodeType(item); - if (type == ttype) { - ret.add(item); - } - } - return ret; - } - - public TreeItem getCurrentTreeItem() { - TreeNode treeNode = (TreeNode) getLastSelectedPathComponent(); - if (treeNode == null) { - return null; - } - return treeNode.getItem(); - } -} +/* + * Copyright (C) 2010-2014 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.ScriptPack; +import com.jpexs.decompiler.flash.gui.abc.treenodes.TreeElement; +import com.jpexs.decompiler.flash.gui.treenodes.SWFNode; +import com.jpexs.decompiler.flash.gui.treenodes.TagTreeRoot; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; +import com.jpexs.decompiler.flash.tags.DefineBitsTag; +import com.jpexs.decompiler.flash.tags.DefineButton2Tag; +import com.jpexs.decompiler.flash.tags.DefineButtonTag; +import com.jpexs.decompiler.flash.tags.DefineEditTextTag; +import com.jpexs.decompiler.flash.tags.DefineFont2Tag; +import com.jpexs.decompiler.flash.tags.DefineFont3Tag; +import com.jpexs.decompiler.flash.tags.DefineFont4Tag; +import com.jpexs.decompiler.flash.tags.DefineFontTag; +import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; +import com.jpexs.decompiler.flash.tags.DefineShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineShape3Tag; +import com.jpexs.decompiler.flash.tags.DefineShape4Tag; +import com.jpexs.decompiler.flash.tags.DefineShapeTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DefineText2Tag; +import com.jpexs.decompiler.flash.tags.DefineTextTag; +import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SoundStreamHead2Tag; +import com.jpexs.decompiler.flash.tags.SoundStreamHeadTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.treeitems.AS2PackageNodeItem; +import com.jpexs.decompiler.flash.treeitems.AS3PackageNodeItem; +import com.jpexs.decompiler.flash.treeitems.FrameNodeItem; +import com.jpexs.decompiler.flash.treeitems.SWFList; +import com.jpexs.decompiler.flash.treeitems.StringItem; +import com.jpexs.decompiler.flash.treeitems.TreeElementItem; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.flash.treenodes.ContainerNode; +import com.jpexs.decompiler.flash.treenodes.FrameNode; +import com.jpexs.decompiler.flash.treenodes.TreeNode; +import com.jpexs.helpers.Helper; +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPopupMenu; +import javax.swing.JTree; +import javax.swing.SwingUtilities; +import javax.swing.plaf.basic.BasicLabelUI; +import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; + +/** + * + * @author JPEXS + */ +public class TagTree extends JTree implements ActionListener { + + private static final String ACTION_RAW_EDIT = "RAWEDIT"; + private static final String ACTION_JUMP_TO_CHARACTER = "JUMPTOCHARACTER"; + private static final String ACTION_REMOVE_ITEM = "REMOVEITEM"; + private static final String ACTION_REMOVE_ITEM_WITH_DEPENDENCIES = "REMOVEITEMWITHDEPENDENCIES"; + private static final String ACTION_CLOSE_SWF = "CLOSESWF"; + private static final String ACTION_EXPAND_RECURSIVE = "EXPANDRECURSIVE"; + + private final MainPanel mainPanel; + + public class TagTreeCellRenderer extends DefaultTreeCellRenderer { + + @Override + public Component getTreeCellRendererComponent( + JTree tree, + Object value, + boolean sel, + boolean expanded, + boolean leaf, + int row, + boolean hasFocus) { + + super.getTreeCellRendererComponent( + tree, value, sel, + expanded, leaf, row, + hasFocus); + TreeNode treeNode = (TreeNode) value; + TreeItem val = treeNode.getItem(); + TreeNodeType type = getTreeNodeType(val); + if (type != null) { + if (type == TreeNodeType.FOLDER && expanded) { + type = TreeNodeType.FOLDER_OPEN; + } + String itemName = type.toString(); + if (type == TreeNodeType.FOLDER || type == TreeNodeType.FOLDER_OPEN) { + if (val instanceof StringItem) { + StringItem si = (StringItem) val; + if (!TagTreeRoot.FOLDER_ROOT.equals(si.getName())) { + itemName = "folder" + si.getName(); + } + } + } + String tagTypeStr = itemName.toLowerCase().replace("_", ""); + setIcon(View.getIcon(tagTypeStr + "16")); + } + + Font font = getFont(); + boolean isModified = false; + if (treeNode instanceof TreeNode) { + if (treeNode.getItem() instanceof Tag) { + Tag tag = (Tag) treeNode.getItem(); + if (tag.isModified()) { + isModified = true; + } + } + } + + if (isModified) { + font = font.deriveFont(Font.BOLD); + } else { + font = font.deriveFont(Font.PLAIN); + } + setFont(font); + + setUI(new BasicLabelUI()); + setOpaque(false); + //setBackground(Color.green); + setBackgroundNonSelectionColor(Color.white); + //setBackgroundSelectionColor(Color.ORANGE); + + return this; + } + } + + TagTree(TagTreeModel treeModel, MainPanel mainPanel) { + super(treeModel); + this.mainPanel = mainPanel; + setCellRenderer(new TagTreeCellRenderer()); + setRootVisible(false); + setBackground(Color.white); + setUI(new BasicTreeUI() { + @Override + public void paint(Graphics g, JComponent c) { + setHashColor(Color.gray); + super.paint(g, c); + } + }); + } + + public static TreeNodeType getTreeNodeType(TreeItem t) { + if ((t instanceof DefineFontTag) + || (t instanceof DefineFont2Tag) + || (t instanceof DefineFont3Tag) + || (t instanceof DefineFont4Tag) + || (t instanceof DefineCompactedFont)) { + return TreeNodeType.FONT; + } + if ((t instanceof DefineTextTag) + || (t instanceof DefineText2Tag) + || (t instanceof DefineEditTextTag)) { + return TreeNodeType.TEXT; + } + + if ((t instanceof DefineBitsTag) + || (t instanceof DefineBitsJPEG2Tag) + || (t instanceof DefineBitsJPEG3Tag) + || (t instanceof DefineBitsJPEG4Tag) + || (t instanceof DefineBitsLosslessTag) + || (t instanceof DefineBitsLossless2Tag)) { + return TreeNodeType.IMAGE; + } + if ((t instanceof DefineShapeTag) + || (t instanceof DefineShape2Tag) + || (t instanceof DefineShape3Tag) + || (t instanceof DefineShape4Tag)) { + return TreeNodeType.SHAPE; + } + + if ((t instanceof DefineMorphShapeTag) || (t instanceof DefineMorphShape2Tag)) { + return TreeNodeType.MORPH_SHAPE; + } + + if (t instanceof DefineSpriteTag) { + return TreeNodeType.SPRITE; + } + if ((t instanceof DefineButtonTag) || (t instanceof DefineButton2Tag)) { + return TreeNodeType.BUTTON; + } + if (t instanceof ASMSource) { + return TreeNodeType.AS; + } + if (t instanceof ScriptPack) { + return TreeNodeType.AS; + } + if (t instanceof AS2PackageNodeItem) { + return TreeNodeType.PACKAGE; + } + if (t instanceof AS3PackageNodeItem) { + return TreeNodeType.PACKAGE; + } + if (t instanceof FrameNodeItem) { + return TreeNodeType.FRAME; + } + if (t instanceof ShowFrameTag) { + return TreeNodeType.SHOW_FRAME; + } + + if (t instanceof DefineVideoStreamTag) { + return TreeNodeType.MOVIE; + } + + if ((t instanceof DefineSoundTag) || (t instanceof SoundStreamHeadTag) || (t instanceof SoundStreamHead2Tag)) { + return TreeNodeType.SOUND; + } + + if (t instanceof DefineBinaryDataTag) { + return TreeNodeType.BINARY_DATA; + } + + if (t instanceof SWF) { + return TreeNodeType.FLASH; + } + + if (t instanceof SWFList) { + SWFList slist = (SWFList) t; + if (slist.name != null) { + if (slist.name.toLowerCase().endsWith(".zip")) { + return TreeNodeType.BUNDLE_ZIP; + } + if (slist.name.toLowerCase().endsWith(".swc")) { + return TreeNodeType.BUNDLE_SWC; + } else { + return TreeNodeType.BUNDLE_BINARY; + } + } + } + + if (t instanceof Tag) { + return TreeNodeType.OTHER_TAG; + } + + return TreeNodeType.FOLDER; + } + + public void createContextMenu(final List swfs) { + final JPopupMenu contextPopupMenu = new JPopupMenu(); + + final JMenuItem expandRecursiveMenuItem = new JMenuItem(mainPanel.translate("contextmenu.expandAll")); + expandRecursiveMenuItem.addActionListener(this); + expandRecursiveMenuItem.setActionCommand(ACTION_EXPAND_RECURSIVE); + contextPopupMenu.add(expandRecursiveMenuItem); + + final JMenuItem removeMenuItem = new JMenuItem(mainPanel.translate("contextmenu.remove")); + removeMenuItem.addActionListener(this); + removeMenuItem.setActionCommand(ACTION_REMOVE_ITEM); + contextPopupMenu.add(removeMenuItem); + + final JMenuItem removeWithDependenciesMenuItem = new JMenuItem(mainPanel.translate("contextmenu.removeWithDependencies")); + removeWithDependenciesMenuItem.addActionListener(this); + removeWithDependenciesMenuItem.setActionCommand(ACTION_REMOVE_ITEM_WITH_DEPENDENCIES); + contextPopupMenu.add(removeWithDependenciesMenuItem); + + final JMenuItem exportSelectionMenuItem = new JMenuItem(mainPanel.translate("menu.file.export.selection")); + exportSelectionMenuItem.setActionCommand(MainFrameRibbonMenu.ACTION_EXPORT_SEL); + exportSelectionMenuItem.addActionListener(mainPanel); + contextPopupMenu.add(exportSelectionMenuItem); + + final JMenuItem replaceSelectionMenuItem = new JMenuItem(mainPanel.translate("button.replace")); + replaceSelectionMenuItem.setActionCommand(MainPanel.ACTION_REPLACE); + replaceSelectionMenuItem.addActionListener(mainPanel); + contextPopupMenu.add(replaceSelectionMenuItem); + + final JMenuItem rawEditMenuItem = new JMenuItem(mainPanel.translate("contextmenu.rawEdit")); + rawEditMenuItem.setActionCommand(ACTION_RAW_EDIT); + rawEditMenuItem.addActionListener(this); + rawEditMenuItem.setVisible(false); + contextPopupMenu.add(rawEditMenuItem); + + final JMenuItem jumpToCharacterMenuItem = new JMenuItem(mainPanel.translate("contextmenu.jumpToCharacter")); + jumpToCharacterMenuItem.setActionCommand(ACTION_JUMP_TO_CHARACTER); + jumpToCharacterMenuItem.addActionListener(this); + jumpToCharacterMenuItem.setVisible(false); + contextPopupMenu.add(jumpToCharacterMenuItem); + + final JMenuItem closeSelectionMenuItem = new JMenuItem(mainPanel.translate("contextmenu.closeSwf")); + closeSelectionMenuItem.setActionCommand(ACTION_CLOSE_SWF); + closeSelectionMenuItem.addActionListener(this); + contextPopupMenu.add(closeSelectionMenuItem); + + final JMenu moveTagMenu = new JMenu(mainPanel.translate("contextmenu.moveTag")); + contextPopupMenu.add(moveTagMenu); + + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (SwingUtilities.isRightMouseButton(e)) { + + int row = getClosestRowForLocation(e.getX(), e.getY()); + int[] selectionRows = getSelectionRows(); + if (!Helper.contains(selectionRows, row)) { + setSelectionRow(row); + } + + TreePath[] paths = getSelectionPaths(); + if (paths == null || paths.length == 0) { + return; + } + boolean allSelectedIsTagOrFrame = true; + for (TreePath treePath : paths) { + TreeNode treeNode = (TreeNode) treePath.getLastPathComponent(); + + TreeItem tag = treeNode.getItem(); + if (!(tag instanceof Tag) && !(tag instanceof FrameNodeItem)) { + allSelectedIsTagOrFrame = false; + break; + } + } + + replaceSelectionMenuItem.setVisible(false); + closeSelectionMenuItem.setVisible(false); + moveTagMenu.setVisible(false); + expandRecursiveMenuItem.setVisible(false); + + if (paths.length == 1) { + TreeNode treeNode = (TreeNode) paths[0].getLastPathComponent(); + + TreeItem item = ((TreeNode) treeNode).getItem(); + + if (item instanceof ImageTag && ((ImageTag) item).importSupported()) { + replaceSelectionMenuItem.setVisible(true); + } + + if (item instanceof DefineBinaryDataTag) { + replaceSelectionMenuItem.setVisible(true); + } + + if (item instanceof DefineSoundTag) { + replaceSelectionMenuItem.setVisible(true); + } + + if (treeNode instanceof SWFNode) { + closeSelectionMenuItem.setVisible(true); + } + + if (item instanceof Tag && swfs.size() > 1) { + final Tag tag = (Tag) item; + moveTagMenu.removeAll(); + for (SWFList targetSwfList : swfs) { + for (final SWF targetSwf : targetSwfList) { + if (targetSwf != tag.getSwf()) { + JMenuItem swfItem = new JMenuItem(targetSwf.getShortFileName()); + swfItem.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent ae) { + tag.getSwf().tags.remove(tag); + tag.setSwf(targetSwf); + targetSwf.tags.add(tag); + mainPanel.refreshTree(); + } + }); + moveTagMenu.add(swfItem); + } + } + } + moveTagMenu.setVisible(true); + } + + TreeModel model = getModel(); + expandRecursiveMenuItem.setVisible(model.getChildCount(treeNode) > 0); + + jumpToCharacterMenuItem.setVisible(item instanceof CharacterIdTag && !(item instanceof CharacterTag)); + + rawEditMenuItem.setVisible(item instanceof Tag); + } + + removeMenuItem.setVisible(allSelectedIsTagOrFrame); + exportSelectionMenuItem.setEnabled(hasExportableNodes()); + contextPopupMenu.show(e.getComponent(), e.getX(), e.getY()); + } + } + }); + } + + @Override + public void actionPerformed(ActionEvent e) { + switch (e.getActionCommand()) { + case ACTION_RAW_EDIT: { + TreeItem item = getCurrentTreeItem(); + if (item == null) { + return; + } + + mainPanel.showGenericTag((Tag) item); + } + break; + case ACTION_JUMP_TO_CHARACTER: { + TreeItem item = getCurrentTreeItem(); + if (item == null || !(item instanceof CharacterIdTag)) { + return; + } + + CharacterIdTag characterIdTag = (CharacterIdTag) item; + mainPanel.setTreeItem(item.getSwf().characters.get(characterIdTag.getCharacterId())); + } + break; + case ACTION_EXPAND_RECURSIVE: { + TreePath path = getSelectionPath(); + if (path == null) { + return; + } + View.expandTreeNodesRecursive(this, path, true); + } + break; + case ACTION_REMOVE_ITEM: + case ACTION_REMOVE_ITEM_WITH_DEPENDENCIES: + List sel = getSelected(this); + + List tagsToRemove = new ArrayList<>(); + for (TreeNode o : sel) { + TreeItem tag = o.getItem(); + if (tag instanceof Tag) { + tagsToRemove.add((Tag) tag); + } else if (tag instanceof FrameNodeItem) { + FrameNodeItem frameNode = (FrameNodeItem) tag; + Frame frame = frameNode.getParent().getTimeline().frames.get(frameNode.getFrame() - 1); + if (frame.showFrameTag != null) { + tagsToRemove.add(frame.showFrameTag); + } else { + // this should be the last frame, so remove the inner tags + tagsToRemove.addAll(frame.innerTags); + } + } + } + + boolean removeDependencies = e.getActionCommand().equals(ACTION_REMOVE_ITEM_WITH_DEPENDENCIES); + if (tagsToRemove.size() == 1) { + Tag tag = tagsToRemove.get(0); + if (View.showConfirmDialog(this, mainPanel.translate("message.confirm.remove").replace("%item%", tag.toString()), mainPanel.translate("message.confirm"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { + tag.getSwf().removeTag(tag, removeDependencies); + mainPanel.refreshTree(); + } + } else if (tagsToRemove.size() > 1) { + if (View.showConfirmDialog(this, mainPanel.translate("message.confirm.removemultiple").replace("%count%", Integer.toString(tagsToRemove.size())), mainPanel.translate("message.confirm"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { + for (Tag tag : tagsToRemove) { + tag.getSwf().removeTag(tag, removeDependencies); + } + mainPanel.refreshTree(); + } + } + break; + case ACTION_CLOSE_SWF: { + Main.closeFile(mainPanel.getCurrentSwfList()); + } + } + } + + public boolean hasExportableNodes() { + return !getSelection(mainPanel.getCurrentSwf()).isEmpty(); + } + + public List getAllSubs(JTree tree, TreeNode o) { + TagTreeModel tm = (TagTreeModel) tree.getModel(); + List ret = new ArrayList<>(); + for (int i = 0; i < tm.getChildCount(o); i++) { + TreeNode c = tm.getChild(o, i); + ret.add(c); + ret.addAll(getAllSubs(tree, c)); + } + return ret; + } + + public List getAllSelected(TagTree tree) { + TreeSelectionModel tsm = tree.getSelectionModel(); + TreePath[] tps = tsm.getSelectionPaths(); + List ret = new ArrayList<>(); + if (tps == null) { + return ret; + } + + for (TreePath tp : tps) { + TreeNode treeNode = (TreeNode) tp.getLastPathComponent(); + ret.add(treeNode); + ret.addAll(getAllSubs(tree, treeNode)); + } + return ret; + } + + public List getSelected(JTree tree) { + TreeSelectionModel tsm = tree.getSelectionModel(); + TreePath[] tps = tsm.getSelectionPaths(); + List ret = new ArrayList<>(); + if (tps == null) { + return ret; + } + + for (TreePath tp : tps) { + TreeNode treeNode = (TreeNode) tp.getLastPathComponent(); + ret.add(treeNode); + } + return ret; + } + + public List getSelection(SWF swf) { + List ret = new ArrayList<>(); + List sel = getAllSelected(this); + for (TreeNode d : sel) { + if (d.getItem().getSwf() != swf) { + continue; + } + if (d instanceof ContainerNode) { + ContainerNode n = (ContainerNode) d; + TreeNodeType nodeType = TagTree.getTreeNodeType(n.getItem()); + if (nodeType == TreeNodeType.IMAGE) { + ret.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.SHAPE) { + ret.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.MORPH_SHAPE) { + ret.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.AS) { + ret.add(n); + } + if (nodeType == TreeNodeType.MOVIE) { + ret.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.SOUND) { + ret.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.BINARY_DATA) { + ret.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.TEXT) { + ret.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.FONT) { + ret.add((Tag) n.getItem()); + } + } + if (d instanceof FrameNode) { + FrameNode fn = (FrameNode) d; + if (!fn.scriptsNode) { + ret.add(d.getItem()); + } + } + if (d instanceof TreeElement) { + if (((TreeElement) d).isLeaf()) { + TreeElement treeElement = (TreeElement) d; + ret.add((ScriptPack) treeElement.getItem()); + } + } + } + return ret; + } + + public List getTagsWithType(List list, TreeNodeType type) { + List ret = new ArrayList<>(); + for (TreeElementItem item : list) { + TreeNodeType ttype = getTreeNodeType(item); + if (type == ttype) { + ret.add(item); + } + } + return ret; + } + + public TreeItem getCurrentTreeItem() { + TreeNode treeNode = (TreeNode) getLastSelectedPathComponent(); + if (treeNode == null) { + return null; + } + return treeNode.getItem(); + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/Tag.java b/src/com/jpexs/decompiler/flash/tags/Tag.java index 04a964d38..7c01411c6 100644 --- a/src/com/jpexs/decompiler/flash/tags/Tag.java +++ b/src/com/jpexs/decompiler/flash/tags/Tag.java @@ -1,442 +1,449 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.tags.base.ContainerItem; -import com.jpexs.decompiler.flash.tags.base.Exportable; -import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; -import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; -import com.jpexs.decompiler.flash.tags.gfx.DefineExternalGradient; -import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage; -import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage2; -import com.jpexs.decompiler.flash.tags.gfx.DefineExternalSound; -import com.jpexs.decompiler.flash.tags.gfx.DefineExternalStreamSound; -import com.jpexs.decompiler.flash.tags.gfx.DefineGradientMap; -import com.jpexs.decompiler.flash.tags.gfx.DefineSubImage; -import com.jpexs.decompiler.flash.tags.gfx.ExporterInfoTag; -import com.jpexs.decompiler.flash.tags.gfx.FontTextureInfo; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.helpers.ByteArrayRange; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.Serializable; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Represents Tag inside SWF file - */ -public abstract class Tag implements NeedsCharacters, Exportable, ContainerItem, Serializable { - - /** - * Identifier of tag type - */ - protected int id; - /** - * If true, then Tag is written to the stream as longer than 0x3f even if it - * is not - */ - @Internal - public boolean forceWriteAsLong = false; - protected String tagName; - @Internal - protected transient SWF swf; - @Internal - protected transient Timelined timelined; - @Internal - private boolean modified; - /** - * Original tag data - */ - @Internal - private ByteArrayRange originalData; - - public String getTagName() { - return tagName; - } - - public String getName() { - return tagName; - } - - @Override - public String getExportFileName() { - return getName(); - } - - /** - * Returns identifier of tag type - * - * @return Identifier of tag type - */ - public int getId() { - return id; - } - - @Override - public SWF getSwf() { - return swf; - } - - public void setSwf(SWF swf) { - this.swf = swf; - } - - public Timelined getTimelined() { - return timelined; - } - - public void setTimelined(Timelined timelined) { - this.timelined = timelined; - } - - /** - * Constructor - * - * @param swf The SWF - * @param id Tag type identifier - * @param name Tag name - * @param data Original tag data - */ - public Tag(SWF swf, int id, String name, ByteArrayRange data) { - this.id = id; - this.tagName = name; - this.originalData = data; - this.swf = swf; - if (swf == null) { - throw new Error("swf parameter cannot be null."); - } - if (data == null) { // it is tag build by constructor - modified = true; - } - } - - private static final Object lockObject = new Object(); - private static List knownTagIds; - private static List requiredTagIds; - - public static List getKnownTags() { - if (knownTagIds == null) { - synchronized (lockObject) { - if (knownTagIds == null) { - List tagIds = Arrays.asList( - CSMTextSettingsTag.ID, - DebugIDTag.ID, - DefineBinaryDataTag.ID, - DefineBitsJPEG2Tag.ID, - DefineBitsJPEG3Tag.ID, - DefineBitsJPEG4Tag.ID, - DefineBitsLossless2Tag.ID, - DefineBitsLosslessTag.ID, - DefineBitsTag.ID, - DefineButton2Tag.ID, - DefineButtonCxformTag.ID, - DefineButtonSoundTag.ID, - DefineButtonTag.ID, - DefineEditTextTag.ID, - DefineFont2Tag.ID, - DefineFont3Tag.ID, - DefineFont4Tag.ID, - DefineFontAlignZonesTag.ID, - DefineFontInfo2Tag.ID, - DefineFontInfoTag.ID, - DefineFontNameTag.ID, - DefineFontTag.ID, - DefineMorphShape2Tag.ID, - DefineMorphShapeTag.ID, - DefineScalingGridTag.ID, - DefineSceneAndFrameLabelDataTag.ID, - DefineShape2Tag.ID, - DefineShape3Tag.ID, - DefineShape4Tag.ID, - DefineShapeTag.ID, - DefineSoundTag.ID, - DefineSpriteTag.ID, - DefineText2Tag.ID, - DefineTextTag.ID, - DefineVideoStreamTag.ID, - DoABCDefineTag.ID, - DoABCTag.ID, - DoActionTag.ID, - DoInitActionTag.ID, - EnableDebugger2Tag.ID, - EnableDebuggerTag.ID, - EnableTelemetryTag.ID, - EndTag.ID, - ExportAssetsTag.ID, - FileAttributesTag.ID, - FrameLabelTag.ID, - ImportAssets2Tag.ID, - ImportAssetsTag.ID, - JPEGTablesTag.ID, - MetadataTag.ID, - PlaceObject2Tag.ID, - PlaceObject3Tag.ID, - PlaceObject4Tag.ID, - PlaceObjectTag.ID, - ProductInfoTag.ID, - ProtectTag.ID, - RemoveObject2Tag.ID, - RemoveObjectTag.ID, - ScriptLimitsTag.ID, - SetBackgroundColorTag.ID, - SetTabIndexTag.ID, - ShowFrameTag.ID, - SoundStreamBlockTag.ID, - SoundStreamHead2Tag.ID, - SoundStreamHeadTag.ID, - StartSound2Tag.ID, - StartSoundTag.ID, - SymbolClassTag.ID, - VideoFrameTag.ID, - DefineCompactedFont.ID, - DefineExternalGradient.ID, - DefineExternalImage.ID, - DefineExternalImage2.ID, - DefineExternalSound.ID, - DefineExternalStreamSound.ID, - DefineGradientMap.ID, - DefineSubImage.ID, - ExporterInfoTag.ID, - FontTextureInfo.ID); - knownTagIds = tagIds; - } - } - } - return knownTagIds; - } - - public static List getRequiredTags() { - if (requiredTagIds == null) { - synchronized (lockObject) { - if (requiredTagIds == null) { - List tagIds = Arrays.asList( - DefineBinaryDataTag.ID, - DefineBitsJPEG2Tag.ID, - DefineBitsJPEG3Tag.ID, - DefineBitsJPEG4Tag.ID, - DefineBitsLossless2Tag.ID, - DefineBitsLosslessTag.ID, - DefineBitsTag.ID, - DefineButton2Tag.ID, - DefineButtonCxformTag.ID, - DefineButtonSoundTag.ID, - DefineButtonTag.ID, - DefineEditTextTag.ID, - DefineFont2Tag.ID, - DefineFont3Tag.ID, - DefineFont4Tag.ID, - DefineFontAlignZonesTag.ID, - DefineFontInfo2Tag.ID, - DefineFontInfoTag.ID, - DefineFontNameTag.ID, - DefineFontTag.ID, - DefineMorphShape2Tag.ID, - DefineMorphShapeTag.ID, - DefineScalingGridTag.ID, - DefineSceneAndFrameLabelDataTag.ID, - DefineShape2Tag.ID, - DefineShape3Tag.ID, - DefineShape4Tag.ID, - DefineShapeTag.ID, - DefineSoundTag.ID, - DefineSpriteTag.ID, - DefineText2Tag.ID, - DefineTextTag.ID, - DefineVideoStreamTag.ID, - DoABCDefineTag.ID, - DoABCTag.ID, - DoActionTag.ID, - DoInitActionTag.ID, - ShowFrameTag.ID); - requiredTagIds = tagIds; - } - } - } - return requiredTagIds; - } - - public int getVersion() { - if (swf == null) { - return SWF.DEFAULT_VERSION; - } - return swf.version; - } - - protected byte[] getHeader(byte[] data) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - - SWFOutputStream sos = new SWFOutputStream(baos, swf.version); - int tagLength = data.length; - int tagID = getId(); - int tagIDLength = (tagID << 6); - if ((tagLength <= 62) && (!forceWriteAsLong)) { - tagIDLength += tagLength; - sos.writeUI16(tagIDLength); - } else { - tagIDLength += 0x3f; - sos.writeUI16(tagIDLength); - sos.writeSI32(tagLength); - } - } catch (IOException iex) { - } - return baos.toByteArray(); - } - - public static byte[] getTagHeader(int tagIDTagLength, long tagLength, boolean writeLong, int version) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - - SWFOutputStream sos = new SWFOutputStream(baos, version); - sos.writeUI16(tagIDTagLength); - if (writeLong) { - sos.writeSI32(tagLength); - } - } catch (IOException iex) { - } - return baos.toByteArray(); - } - - /** - * Writes Tag value to the stream - * - * @param sos SWF output stream - * @throws IOException - */ - public void writeTag(SWFOutputStream sos) throws IOException { - if (isModified()) { - byte[] newData = getData(); - byte[] newHeaderData = getHeader(newData); - sos.write(newHeaderData); - sos.write(newData); - } else { - sos.write(originalData.array, originalData.pos, originalData.length); - } - } - - /** - * Returns string representation of the object - * - * @return String representation of the object - */ - @Override - public String toString() { - return getName(); - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - public byte[] getData() { - return getOriginalData(); - } - - public final ByteArrayRange getOriginalRange() { - return originalData; - } - - public final byte[] getOriginalData() { - // todo honfika: do not copy data - int dataLength = getOriginalDataLength(); - byte[] data = new byte[dataLength]; - System.arraycopy(originalData.array, (int) (originalData.pos + originalData.length - dataLength), data, 0, dataLength); - return data; - } - - public final int getOriginalDataLength() { - return originalData.length - (isLongOriginal() ? 6 : 2); - } - - private final boolean isLongOriginal() { - int shortLength = originalData.array[(int) originalData.pos] & 0x003F; - return shortLength == 0x3f; - } - - public boolean hasSubTags() { - return false; - } - - public List getSubTags() { - return null; - } - - public long getPos() { - return originalData.pos; - } - - public long getDataPos() { - return originalData.pos + (isLongOriginal() ? 6 : 2); - } - - public void setModified(boolean value) { - modified = value; - } - - public void createOriginalData() { - byte[] data = getData(); - byte[] headerData = getHeader(data); - byte[] tagData = new byte[data.length + headerData.length]; - System.arraycopy(headerData, 0, tagData, 0, headerData.length); - System.arraycopy(data, 0, tagData, headerData.length, data.length); - originalData = new ByteArrayRange(tagData, 0, tagData.length); - } - - public boolean isModified() { - return modified; - } - - @Override - public void getNeededCharacters(Set needed) { - } - - @Override - public boolean removeCharacter(int characterId) { - return false; - } - - public void getNeededCharactersDeep(Set needed) { - Set visited = new HashSet<>(); - Set needed2 = new HashSet<>(); - getNeededCharacters(needed2); - - while (visited.size() != needed2.size()) { - for (Integer characterId : needed2) { - if (!visited.contains(characterId)) { - visited.add(characterId); - if (swf.characters.containsKey(characterId)) { - swf.characters.get(characterId).getNeededCharacters(needed2); - break; - } - } - } - } - - for (Integer characterId : needed2) { - if (swf.characters.containsKey(characterId)) { - needed.add(characterId); - } - } - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.tags.base.ContainerItem; +import com.jpexs.decompiler.flash.tags.base.Exportable; +import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; +import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalGradient; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage2; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalSound; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalStreamSound; +import com.jpexs.decompiler.flash.tags.gfx.DefineGradientMap; +import com.jpexs.decompiler.flash.tags.gfx.DefineSubImage; +import com.jpexs.decompiler.flash.tags.gfx.ExporterInfoTag; +import com.jpexs.decompiler.flash.tags.gfx.FontTextureInfo; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.helpers.ByteArrayRange; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.Serializable; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Represents Tag inside SWF file + */ +public abstract class Tag implements NeedsCharacters, Exportable, ContainerItem, Serializable { + + /** + * Identifier of tag type + */ + protected int id; + /** + * If true, then Tag is written to the stream as longer than 0x3f even if it + * is not + */ + @Internal + public boolean forceWriteAsLong = false; + protected String tagName; + @Internal + protected transient SWF swf; + @Internal + protected transient Timelined timelined; + @Internal + private boolean modified; + /** + * Original tag data + */ + @Internal + private ByteArrayRange originalData; + + public String getTagName() { + return tagName; + } + + public String getName() { + return tagName; + } + + @Override + public String getExportFileName() { + return getName(); + } + + /** + * Returns identifier of tag type + * + * @return Identifier of tag type + */ + public int getId() { + return id; + } + + @Override + public SWF getSwf() { + return swf; + } + + public void setSwf(SWF swf) { + this.swf = swf; + } + + public Timelined getTimelined() { + return timelined; + } + + public void setTimelined(Timelined timelined) { + this.timelined = timelined; + } + + /** + * Constructor + * + * @param swf The SWF + * @param id Tag type identifier + * @param name Tag name + * @param data Original tag data + */ + public Tag(SWF swf, int id, String name, ByteArrayRange data) { + this.id = id; + this.tagName = name; + this.originalData = data; + this.swf = swf; + if (swf == null) { + throw new Error("swf parameter cannot be null."); + } + if (data == null) { // it is tag build by constructor + modified = true; + } + } + + private static final Object lockObject = new Object(); + private static List knownTagIds; + private static List requiredTagIds; + + public static List getKnownTags() { + if (knownTagIds == null) { + synchronized (lockObject) { + if (knownTagIds == null) { + List tagIds = Arrays.asList( + CSMTextSettingsTag.ID, + DebugIDTag.ID, + DefineBinaryDataTag.ID, + DefineBitsJPEG2Tag.ID, + DefineBitsJPEG3Tag.ID, + DefineBitsJPEG4Tag.ID, + DefineBitsLossless2Tag.ID, + DefineBitsLosslessTag.ID, + DefineBitsTag.ID, + DefineButton2Tag.ID, + DefineButtonCxformTag.ID, + DefineButtonSoundTag.ID, + DefineButtonTag.ID, + DefineEditTextTag.ID, + DefineFont2Tag.ID, + DefineFont3Tag.ID, + DefineFont4Tag.ID, + DefineFontAlignZonesTag.ID, + DefineFontInfo2Tag.ID, + DefineFontInfoTag.ID, + DefineFontNameTag.ID, + DefineFontTag.ID, + DefineMorphShape2Tag.ID, + DefineMorphShapeTag.ID, + DefineScalingGridTag.ID, + DefineSceneAndFrameLabelDataTag.ID, + DefineShape2Tag.ID, + DefineShape3Tag.ID, + DefineShape4Tag.ID, + DefineShapeTag.ID, + DefineSoundTag.ID, + DefineSpriteTag.ID, + DefineText2Tag.ID, + DefineTextTag.ID, + DefineVideoStreamTag.ID, + DoABCDefineTag.ID, + DoABCTag.ID, + DoActionTag.ID, + DoInitActionTag.ID, + EnableDebugger2Tag.ID, + EnableDebuggerTag.ID, + EnableTelemetryTag.ID, + EndTag.ID, + ExportAssetsTag.ID, + FileAttributesTag.ID, + FrameLabelTag.ID, + ImportAssets2Tag.ID, + ImportAssetsTag.ID, + JPEGTablesTag.ID, + MetadataTag.ID, + PlaceObject2Tag.ID, + PlaceObject3Tag.ID, + PlaceObject4Tag.ID, + PlaceObjectTag.ID, + ProductInfoTag.ID, + ProtectTag.ID, + RemoveObject2Tag.ID, + RemoveObjectTag.ID, + ScriptLimitsTag.ID, + SetBackgroundColorTag.ID, + SetTabIndexTag.ID, + ShowFrameTag.ID, + SoundStreamBlockTag.ID, + SoundStreamHead2Tag.ID, + SoundStreamHeadTag.ID, + StartSound2Tag.ID, + StartSoundTag.ID, + SymbolClassTag.ID, + VideoFrameTag.ID, + DefineCompactedFont.ID, + DefineExternalGradient.ID, + DefineExternalImage.ID, + DefineExternalImage2.ID, + DefineExternalSound.ID, + DefineExternalStreamSound.ID, + DefineGradientMap.ID, + DefineSubImage.ID, + ExporterInfoTag.ID, + FontTextureInfo.ID); + knownTagIds = tagIds; + } + } + } + return knownTagIds; + } + + public static List getRequiredTags() { + if (requiredTagIds == null) { + synchronized (lockObject) { + if (requiredTagIds == null) { + List tagIds = Arrays.asList( + DefineBinaryDataTag.ID, + DefineBitsJPEG2Tag.ID, + DefineBitsJPEG3Tag.ID, + DefineBitsJPEG4Tag.ID, + DefineBitsLossless2Tag.ID, + DefineBitsLosslessTag.ID, + DefineBitsTag.ID, + DefineButton2Tag.ID, + DefineButtonCxformTag.ID, + DefineButtonSoundTag.ID, + DefineButtonTag.ID, + DefineEditTextTag.ID, + DefineFont2Tag.ID, + DefineFont3Tag.ID, + DefineFont4Tag.ID, + DefineFontAlignZonesTag.ID, + DefineFontInfo2Tag.ID, + DefineFontInfoTag.ID, + DefineFontNameTag.ID, + DefineFontTag.ID, + DefineMorphShape2Tag.ID, + DefineMorphShapeTag.ID, + DefineScalingGridTag.ID, + DefineSceneAndFrameLabelDataTag.ID, + DefineShape2Tag.ID, + DefineShape3Tag.ID, + DefineShape4Tag.ID, + DefineShapeTag.ID, + DefineSoundTag.ID, + DefineSpriteTag.ID, + DefineText2Tag.ID, + DefineTextTag.ID, + DefineVideoStreamTag.ID, + DoABCDefineTag.ID, + DoABCTag.ID, + DoActionTag.ID, + DoInitActionTag.ID, + ShowFrameTag.ID); + requiredTagIds = tagIds; + } + } + } + return requiredTagIds; + } + + public int getVersion() { + if (swf == null) { + return SWF.DEFAULT_VERSION; + } + return swf.version; + } + + protected byte[] getHeader(byte[] data) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + + SWFOutputStream sos = new SWFOutputStream(baos, swf.version); + int tagLength = data.length; + int tagID = getId(); + int tagIDLength = (tagID << 6); + if ((tagLength <= 62) && (!forceWriteAsLong)) { + tagIDLength += tagLength; + sos.writeUI16(tagIDLength); + } else { + tagIDLength += 0x3f; + sos.writeUI16(tagIDLength); + sos.writeSI32(tagLength); + } + } catch (IOException iex) { + } + return baos.toByteArray(); + } + + public static byte[] getTagHeader(int tagIDTagLength, long tagLength, boolean writeLong, int version) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + + SWFOutputStream sos = new SWFOutputStream(baos, version); + sos.writeUI16(tagIDTagLength); + if (writeLong) { + sos.writeSI32(tagLength); + } + } catch (IOException iex) { + } + return baos.toByteArray(); + } + + /** + * Writes Tag value to the stream + * + * @param sos SWF output stream + * @throws IOException + */ + public void writeTag(SWFOutputStream sos) throws IOException { + if (isModified()) { + byte[] newData = getData(); + byte[] newHeaderData = getHeader(newData); + sos.write(newHeaderData); + sos.write(newData); + } else { + sos.write(originalData.array, originalData.pos, originalData.length); + } + } + + /** + * Returns string representation of the object + * + * @return String representation of the object + */ + @Override + public String toString() { + return getName(); + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + public byte[] getData() { + return getOriginalData(); + } + + public final ByteArrayRange getOriginalRange() { + return originalData; + } + + public final byte[] getOriginalData() { + if (originalData == null) { + return new byte[0]; + } + // todo honfika: do not copy data + int dataLength = getOriginalDataLength(); + byte[] data = new byte[dataLength]; + System.arraycopy(originalData.array, (int) (originalData.pos + originalData.length - dataLength), data, 0, dataLength); + return data; + } + + public final int getOriginalDataLength() { + if (originalData == null) { + return 0; + } + + return originalData.length - (isLongOriginal() ? 6 : 2); + } + + private final boolean isLongOriginal() { + int shortLength = originalData.array[(int) originalData.pos] & 0x003F; + return shortLength == 0x3f; + } + + public boolean hasSubTags() { + return false; + } + + public List getSubTags() { + return null; + } + + public long getPos() { + return originalData.pos; + } + + public long getDataPos() { + return originalData.pos + (isLongOriginal() ? 6 : 2); + } + + public void setModified(boolean value) { + modified = value; + } + + public void createOriginalData() { + byte[] data = getData(); + byte[] headerData = getHeader(data); + byte[] tagData = new byte[data.length + headerData.length]; + System.arraycopy(headerData, 0, tagData, 0, headerData.length); + System.arraycopy(data, 0, tagData, headerData.length, data.length); + originalData = new ByteArrayRange(tagData, 0, tagData.length); + } + + public boolean isModified() { + return modified; + } + + @Override + public void getNeededCharacters(Set needed) { + } + + @Override + public boolean removeCharacter(int characterId) { + return false; + } + + public void getNeededCharactersDeep(Set needed) { + Set visited = new HashSet<>(); + Set needed2 = new HashSet<>(); + getNeededCharacters(needed2); + + while (visited.size() != needed2.size()) { + for (Integer characterId : needed2) { + if (!visited.contains(characterId)) { + visited.add(characterId); + if (swf.characters.containsKey(characterId)) { + swf.characters.get(characterId).getNeededCharacters(needed2); + break; + } + } + } + } + + for (Integer characterId : needed2) { + if (swf.characters.containsKey(characterId)) { + needed.add(characterId); + } + } + } +}