From 29f52c228303385b9ef4bcc6d892bcff044b266a Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Sat, 1 Aug 2015 17:52:28 +0200 Subject: [PATCH] #1007 Allow to replace binary data (ByteArrayRange) in raw editing --- .../flash/gui/GenericTagTreePanel.java | 2273 +++--- .../jpexs/decompiler/flash/gui/MainPanel.java | 6738 ++++++++--------- .../decompiler/flash/gui/PreviewPanel.java | 2378 +++--- 3 files changed, 5703 insertions(+), 5686 deletions(-) diff --git a/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java b/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java index 2282682c0..333994e84 100644 --- a/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/GenericTagTreePanel.java @@ -1,1128 +1,1145 @@ -/* - * Copyright (C) 2010-2015 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.gui.generictageditors.BooleanEditor; -import com.jpexs.decompiler.flash.gui.generictageditors.ColorEditor; -import com.jpexs.decompiler.flash.gui.generictageditors.GenericTagEditor; -import com.jpexs.decompiler.flash.gui.generictageditors.NumberEditor; -import com.jpexs.decompiler.flash.gui.generictageditors.StringEditor; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.types.ARGB; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.HideInRawEdit; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.flash.types.annotations.Multiline; -import com.jpexs.decompiler.flash.types.annotations.SWFArray; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.decompiler.flash.types.annotations.Table; -import com.jpexs.decompiler.flash.types.annotations.parser.AnnotationParseException; -import com.jpexs.decompiler.flash.types.annotations.parser.ConditionEvaluator; -import com.jpexs.helpers.ByteArrayRange; -import com.jpexs.helpers.ConcreteClasses; -import com.jpexs.helpers.ReflectionTools; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import static java.awt.Component.TOP_ALIGNMENT; -import java.awt.Dimension; -import java.awt.FlowLayout; -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.io.IOException; -import java.lang.reflect.Array; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.EventObject; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.AbstractCellEditor; -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; -import javax.swing.JTree; -import javax.swing.event.TreeModelListener; -import javax.swing.plaf.basic.BasicLabelUI; -import javax.swing.plaf.basic.BasicTreeUI; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeCellRenderer; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreeCellEditor; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; - -/** - * - * @author JPEXS - */ -public class GenericTagTreePanel extends GenericTagPanel { - - private static final Logger logger = Logger.getLogger(GenericTagTreePanel.class.getName()); - - private JTree tree; - - private Tag editedTag; - - private static final Map> fieldCache = new HashMap<>(); - - private static final int FIELD_INDEX = 0; - - private class MyTree extends JTree { - - public MyTree() { - setBackground(Color.white); - setUI(new BasicTreeUI() { - - @Override - public void paint(Graphics g, JComponent c) { - setHashColor(Color.gray); - super.paint(g, c); - } - - }); - setCellRenderer(new MyTreeCellRenderer()); - setCellEditor(new MyTreeCellEditor(this)); - setInvokesStopCellEditing(true); - - } - } - - private class MyTreeCellEditor extends AbstractCellEditor implements TreeCellEditor { - - private List editors = null; - - private final JTree tree; - - private FieldNode fnode; - - public MyTreeCellEditor(JTree tree) { - this.tree = tree; - } - - @Override - public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { - if (value instanceof FieldNode) { - fnode = (FieldNode) value; - JPanel panSum = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - panSum.setOpaque(false); - for (int i = 0; i < fnode.fieldSet.size(); i++) { - Field field = fnode.fieldSet.get(i);//fnode.fieldSet.get(FIELD_INDEX); - int index = fnode.index; - Object obj = fnode.obj; - Class type; - try { - type = ReflectionTools.getValue(obj, field, index).getClass(); - } catch (IllegalArgumentException | IllegalAccessException ex) { - logger.log(Level.SEVERE, "Fixing characters order failed, recursion detected."); - return null; - } - GenericTagEditor editor = null; - SWFType swfType = field.getAnnotation(SWFType.class); - Multiline multiline = field.getAnnotation(Multiline.class); - if (type.equals(int.class) || type.equals(Integer.class) - || type.equals(short.class) || type.equals(Short.class) - || type.equals(long.class) || type.equals(Long.class) - || type.equals(double.class) || type.equals(Double.class) - || type.equals(float.class) || type.equals(Float.class)) { - editor = new NumberEditor(field.getName(), obj, field, index, type, swfType); - } else if (type.equals(boolean.class) || type.equals(Boolean.class)) { - editor = new BooleanEditor(field.getName(), obj, field, index, type); - } else if (type.equals(String.class)) { - editor = new StringEditor(field.getName(), obj, field, index, type, multiline != null); - } else if (type.equals(RGB.class) || type.equals(RGBA.class) || type.equals(ARGB.class)) { - editor = new ColorEditor(field.getName(), obj, field, index, type); - } - if (editor != null) { - if (editors == null) { - editors = new ArrayList<>(); - } - editors.add(editor); - } - JPanel pan = new JPanel(); - FlowLayout fl = new FlowLayout(FlowLayout.LEFT, 0, 0); - fl.setAlignOnBaseline(true); - pan.setLayout(fl); - JLabel nameLabel = new JLabel(fnode.getNameType(i) + " = ") { - - @Override - public BaselineResizeBehavior getBaselineResizeBehavior() { - return Component.BaselineResizeBehavior.CONSTANT_ASCENT; - } - - @Override - public int getBaseline(int width, int height) { - return 0; - } - - }; - pan.setBackground(Color.white); - nameLabel.setAlignmentY(TOP_ALIGNMENT); - pan.add(nameLabel); - - JComponent editorComponent = (JComponent) editor; - if (editorComponent != null) { - nameLabel.setSize(nameLabel.getWidth(), editorComponent.getHeight()); - editorComponent.setAlignmentY(TOP_ALIGNMENT); - pan.add(editorComponent); - pan.setPreferredSize(new Dimension((int) nameLabel.getPreferredSize().getWidth() + 5 + (int) editorComponent.getPreferredSize().getWidth(), (int) editorComponent.getPreferredSize().getHeight())); - } else { - pan.setPreferredSize(new Dimension((int) nameLabel.getPreferredSize().getWidth(), (int) nameLabel.getPreferredSize().getHeight())); - } - panSum.add(pan); - } - return panSum; - } - return null; - - } - - @Override - public Object getCellEditorValue() { - List ret = new ArrayList<>(); - if (editors != null) { - for (GenericTagEditor editor : editors) { - ret.add(editor.getChangedValue()); - } - } - return ret; - } - - @Override - public boolean isCellEditable(EventObject e) { - if (!(e instanceof MouseEvent)) { - return false; - } - - MouseEvent me = (MouseEvent) e; - TreePath path = tree.getPathForLocation(me.getX(), me.getY()); - - if (path == null) { - return false; - } - Object obj = path.getLastPathComponent(); - - boolean ret = super.isCellEditable(e) - && tree.getModel().isLeaf(obj); - return ret; - } - - @Override - public boolean stopCellEditing() { - super.stopCellEditing(); - - /*List depends = ((MyTreeModel) tree.getModel()).getDependentFields(fnode); - boolean dep = false; - if (!depends.isEmpty()) { - dep = true; - } */ - if (editors != null) { - for (GenericTagEditor editor : editors) { - editor.save(); - } - } - TreePath sp = tree.getSelectionPath(); - if (sp != null) { - ((MyTreeModel) tree.getModel()).vchanged(sp); - } - refreshTree(); - return true; - } - } - - public GenericTagTreePanel() { - setLayout(new BorderLayout()); - tree = new MyTree(); - - add(new JScrollPane(tree), BorderLayout.CENTER); - tree.addMouseListener(new MouseAdapter() { - @Override - public void mousePressed(MouseEvent e) { - if (!tree.isEditable()) { - return; - } - int selRow = tree.getRowForLocation(e.getX(), e.getY()); - TreePath selPath = tree.getPathForLocation(e.getX(), e.getY()); - if (selRow != -1 && selPath != null) { - if (e.getClickCount() == 1) { - if (e.getButton() == MouseEvent.BUTTON3) { //right click - Object selObject = selPath.getLastPathComponent(); - if (selObject instanceof FieldNode) { - final FieldNode fnode = (FieldNode) selObject; - Field field = fnode.fieldSet.get(FIELD_INDEX); - if (field.getType().equals(ByteArrayRange.class)) { - // todo: load file (Issue #1007) - } else if (ReflectionTools.needsIndex(field)) { - SWFArray swfArray = fnode.fieldSet.get(FIELD_INDEX).getAnnotation(SWFArray.class); - - String itemStr = ""; - if (swfArray != null) { - itemStr = swfArray.value(); - } - if (fnode.fieldSet.itemName != null && !fnode.fieldSet.itemName.isEmpty()) { - itemStr = fnode.fieldSet.itemName; - } - if (itemStr.isEmpty()) { - itemStr = AppStrings.translate("generictag.array.item"); - } - - boolean canAdd = true; - if (!ReflectionTools.canAddToField(fnode.obj, fnode.fieldSet.get(FIELD_INDEX))) { - canAdd = false; - } - JPopupMenu p = new JPopupMenu(); - JMenuItem mi; - Class subtype = ReflectionTools.getFieldSubType(fnode.obj, fnode.fieldSet.get(FIELD_INDEX)); - if (!canAdd && subtype.isAnnotationPresent(ConcreteClasses.class)) { - Class[] availableClasses = subtype.getAnnotation(ConcreteClasses.class).value(); - JMenu mBegin = new JMenu(AppStrings.translate("generictag.array.insertbeginning").replace("%item%", itemStr)); - p.add(mBegin); - JMenu mBefore = new JMenu(AppStrings.translate("generictag.array.insertbefore").replace("%item%", itemStr)); - p.add(mBefore); - mi = new JMenuItem(AppStrings.translate("generictag.array.remove").replace("%item%", itemStr)); - mi.addActionListener((ActionEvent e1) -> { - removeItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), fnode.index); - }); - p.add(mi); - JMenu mAfter = new JMenu(AppStrings.translate("generictag.array.insertafter").replace("%item%", itemStr)); - p.add(mAfter); - - JMenu mEnd = new JMenu(AppStrings.translate("generictag.array.insertend").replace("%item%", itemStr)); - p.add(mEnd); - - for (Class c : availableClasses) { - mi = new JMenuItem(c.getSimpleName()); - mi.addActionListener((ActionEvent e1) -> { - addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), 0, c); - }); - mBegin.add(mi); - - mi = new JMenuItem(c.getSimpleName()); - mi.addActionListener((ActionEvent e1) -> { - addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), fnode.index, c); - }); - mBefore.add(mi); - - mi = new JMenuItem(c.getSimpleName()); - mi.addActionListener((ActionEvent e1) -> { - addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), fnode.index + 1, c); - }); - mAfter.add(mi); - - mi = new JMenuItem(c.getSimpleName()); - mi.addActionListener((ActionEvent e1) -> { - addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), ReflectionTools.getFieldSubSize(fnode.obj, fnode.fieldSet.get(FIELD_INDEX)), c); - }); - mEnd.add(mi); - } - } else { - - mi = new JMenuItem(AppStrings.translate("generictag.array.insertbeginning").replace("%item%", itemStr)); - mi.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), 0, null); - } - }); - if (!canAdd) { - mi.setEnabled(false); - } - p.add(mi); - - if (fnode.index > -1) { - mi = new JMenuItem(AppStrings.translate("generictag.array.insertbefore").replace("%item%", itemStr)); - mi.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), fnode.index, null); - } - }); - if (!canAdd) { - mi.setEnabled(false); - } - p.add(mi); - - mi = new JMenuItem(AppStrings.translate("generictag.array.remove").replace("%item%", itemStr)); - mi.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - removeItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), fnode.index); - } - }); - p.add(mi); - - mi = new JMenuItem(AppStrings.translate("generictag.array.insertafter").replace("%item%", itemStr)); - mi.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), fnode.index + 1, null); - } - }); - if (!canAdd) { - mi.setEnabled(false); - } - p.add(mi); - } - - mi = new JMenuItem(AppStrings.translate("generictag.array.insertend").replace("%item%", itemStr)); - mi.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), ReflectionTools.getFieldSubSize(fnode.obj, fnode.fieldSet.get(FIELD_INDEX)), null); - } - }); - if (!canAdd) { - mi.setEnabled(false); - } - p.add(mi); - //} - } - p.show(tree, e.getX(), e.getY()); - } - } - } - //} else if (e.getClickCount() == 2) { - // myDoubleClick(selRow, selPath); - } - } - } - }); - } - - private Tag tag; - - public class MyTreeCellRenderer extends DefaultTreeCellRenderer { - - public MyTreeCellRenderer() { - setUI(new BasicLabelUI()); - setOpaque(false); - setBackgroundNonSelectionColor(Color.white); - } - } - - @Override - public void clear() { - - } - - private static final class TableFieldNodes extends DefaultMutableTreeNode { - - List subnodes; - - public TableFieldNodes(List subnodes) { - this.subnodes = subnodes; - } - } - - private static final class FieldNode extends DefaultMutableTreeNode { - - private Object obj; - - private FieldSet fieldSet; - - private int index; - - public FieldNode(Object obj, FieldSet fieldSet, int index) { - this.obj = obj; - this.fieldSet = fieldSet; - this.index = index; - - for (int i = 0; i < fieldSet.size(); i++) { - if (getValue(i) == null) { - try { - if (List.class.isAssignableFrom(fieldSet.get(i).getType())) { - ReflectionTools.setValue(obj, fieldSet.get(i), new ArrayList<>()); - } else if (fieldSet.get(i).getType().isArray()) { - ReflectionTools.setValue(obj, fieldSet.get(i), Array.newInstance(fieldSet.get(i).getType().getComponentType(), 0)); - } - } catch (IllegalArgumentException | IllegalAccessException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - } - } - - @Override - public void setUserObject(Object userObject) { - - } - - /* - */ - @Override - public String toString() { - String ret = ""; - if (index > -1) { - for (int i = 0; i < fieldSet.size(); i++) { - if (i > 0) { - ret += ", "; - } - ret += toString(i); - } - return ret; - } - - if (fieldSet.size() == 1) { - ret = toString(0); - } else { - ret = fieldSet.name; - SWFArray t = fieldSet.get(0).getAnnotation(SWFArray.class); - if (t != null) { - ret += " [" + t.countField() + "]"; - } else { - ret += " []"; - } - } - - return "" + ret + ""; - } - - public String toString(int fieldIndex) { - String valStr = ""; - if (ReflectionTools.needsIndex(fieldSet.get(fieldIndex)) && (index == -1)) { - valStr += ""; - } else if (hasEditor(obj, fieldSet.get(fieldIndex), index)) { - Object val = getValue(fieldIndex); - Color color = null; - String colorAdd = ""; - if (val instanceof RGB) { //Note: Can be RGBA too - color = ((RGB) val).toColor(); - } - if (val instanceof ARGB) { - color = ((ARGB) val).toColor(); - } - - if (color != null) { - colorAdd = "\u25cf "; - } - - if (val instanceof ByteArrayRange) { - valStr += " = " + ((ByteArrayRange) val).getLength() + " byte"; - } else { - valStr += " = " + colorAdd + val.toString(); - } - } - return getNameType(fieldIndex) + valStr; - } - - public String getType(int fieldIndex) { - SWFType swfType = fieldSet.get(fieldIndex).getAnnotation(SWFType.class); - SWFArray swfArray = fieldSet.get(fieldIndex).getAnnotation(SWFArray.class); - String typeStr = null; - if ((swfType != null || swfArray != null) && !(ReflectionTools.needsIndex(fieldSet.get(fieldIndex)) && (index > -1))) { - Class type = fieldSet.get(fieldIndex).getType(); - if (ReflectionTools.needsIndex(fieldSet.get(fieldIndex))) { - type = ReflectionTools.getFieldSubType(obj, fieldSet.get(fieldIndex)); - } - typeStr = swfTypeToString(type, swfType, swfArray); - } - return typeStr; - } - - public String getNameType(int fieldIndex) { - String typeStr = getType(fieldIndex); - return getName(fieldIndex) + (typeStr != null ? " : " + typeStr : ""); - } - - public String getName(int fieldIndex) { - SWFArray swfArray = fieldSet.get(fieldIndex).getAnnotation(SWFArray.class); - String name = ""; - if (swfArray != null) { - name = swfArray.value(); - } - - Object val = null; - try { - if (index > -1) { - val = ReflectionTools.getValue(obj, fieldSet.get(fieldIndex), index); - } - } catch (IllegalArgumentException | IllegalAccessException ex) { - //ignore - } - String typeAdd = ""; - if (val != null) { - typeAdd = " : " + val.getClass().getSimpleName(); - } - - return (index > -1 ? name + "[" + index + "]" + typeAdd : fieldSet.get(fieldIndex).getName()); - } - - public Object getValue(int fieldIndex) { - try { - if (ReflectionTools.needsIndex(fieldSet.get(fieldIndex)) && (index == -1)) { - return ReflectionTools.getValue(obj, fieldSet.get(fieldIndex)); - } - Object val = ReflectionTools.getValue(obj, fieldSet.get(fieldIndex), index); - if (val == null) { - try { - val = ReflectionTools.newInstanceOf(fieldSet.get(fieldIndex).getType()); - ReflectionTools.setValue(obj, fieldSet.get(fieldIndex), index, val); - } catch (InstantiationException | IllegalAccessException ex) { - logger.log(Level.SEVERE, null, ex); - return null; - } - } - return val; - } catch (IllegalArgumentException | IllegalAccessException ex) { - return null; - } - } - - @Override - public int hashCode() { - int hash = 7; - hash = 11 * hash + Objects.hashCode(this.obj); - hash = 11 * hash + Objects.hashCode(this.fieldSet.get(FIELD_INDEX)); - hash = 11 * hash + this.index; - return hash; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final FieldNode other = (FieldNode) obj; - if (!Objects.equals(this.obj, other.obj)) { - return false; - } - if (!Objects.equals(this.fieldSet.get(FIELD_INDEX), other.fieldSet.get(FIELD_INDEX))) { - return false; - } - return this.index == other.index; - } - } - - private static class MyTreeModel extends DefaultTreeModel { - - private final Object mtroot; - - private final List listeners = new ArrayList<>(); - - private final Map nodeCache = new HashMap<>(); - - // it is much faster to store the reverse mappings, too - private final Map nodeCacheReverse = new HashMap<>(); - - private Object getNodeByPath(String path) { - - if (nodeCache.containsKey(path)) { - return nodeCache.get(path); - } - return null; - } - - public String getNodePathName(Object find) { - - if (nodeCacheReverse.containsKey(find)) { - return nodeCacheReverse.get(find); - } - return null; - } - - public List getDependentFields(FieldNode fnode) { - List ret = new ArrayList<>(); - getDependentFields(getNodePathName(fnode), mtroot.getClass().getSimpleName(), mtroot, ret); - return ret; - } - - public void getDependentFields(String dependence, String currentPath, Object node, List ret) { - if (node instanceof FieldNode) { - FieldNode fnode = (FieldNode) node; - Conditional cond = fnode.fieldSet.get(FIELD_INDEX).getAnnotation(Conditional.class); - if (cond != null) { - ConditionEvaluator ev = new ConditionEvaluator(cond); - String parentPath = currentPath.indexOf('.') == -1 ? "" : currentPath.substring(0, currentPath.lastIndexOf('.')); - try { - for (String cname : ev.getFields()) { - String fullParh = parentPath + "." + cname; - if (fullParh.equals(dependence)) { - ret.add(fnode); - break; - } - } - } catch (AnnotationParseException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - } - int count = getChildCount(node); - for (int i = 0; i < count; i++) { - FieldNode f = (FieldNode) getChild(node, i); - getDependentFields(dependence, currentPath + "." + f.getName(FIELD_INDEX), f, ret); - } - } - - public MyTreeModel(Tag root) { - super(new DefaultMutableTreeNode(root)); - this.mtroot = root; - buildCache(root, ""); - } - - private void buildCache(Object obj, String parentPath) { - if (!"".equals(parentPath)) { - parentPath += "."; - } - if (obj instanceof FieldNode) { - FieldNode fn = (FieldNode) obj; - parentPath += fn.getName(FIELD_INDEX); - } else { - parentPath += obj.getClass().getSimpleName(); - } - nodeCache.put(parentPath, obj); - nodeCacheReverse.put(obj, parentPath); - int count = getChildCount(obj, false); - for (int i = 0; i < count; i++) { - buildCache(getChild(obj, i, false), parentPath); - } - } - - @Override - public Object getRoot() { - return mtroot; - } - - private Object getChild(Object parent, int index, boolean limited) { - if (parent == mtroot) { - return new FieldNode(mtroot, filterFields(this, mtroot.getClass().getSimpleName(), mtroot.getClass(), limited).get(index), -1); - } - FieldNode fnode = (FieldNode) parent; - Field field = fnode.fieldSet.get(FIELD_INDEX); - if (ReflectionTools.needsIndex(field) && (fnode.index == -1)) { //Arrays ot Lists - return new FieldNode(fnode.obj, fnode.fieldSet, index); - } - parent = fnode.getValue(FIELD_INDEX); - return new FieldNode(parent, filterFields(this, getNodePathName(fnode), parent.getClass(), limited).get(index), -1); - } - - @Override - public Object getChild(Object parent, int index) { - return getChild(parent, index, true); - } - - @Override - public int getChildCount(Object parent) { - return getChildCount(parent, true); - } - - private int getChildCount(Object parent, boolean limited) { - if (parent == mtroot) { - return filterFields(this, mtroot.getClass().getSimpleName(), mtroot.getClass(), limited).size(); - } - FieldNode fnode = (FieldNode) parent; - if (isLeaf(fnode)) { - return 0; - } - Field field = fnode.fieldSet.get(FIELD_INDEX); - if (ReflectionTools.needsIndex(field) && (fnode.index == -1)) { //Arrays or Lists - try { - if (field.get(fnode.obj) == null) { - // todo: instanciate the (Array)List or Array to allow adding items to it - return 0; - } - } catch (IllegalArgumentException | IllegalAccessException ex) { - return 0; - } - - return ReflectionTools.getFieldSubSize(fnode.obj, field); - } - parent = fnode.getValue(FIELD_INDEX); - - return filterFields(this, getNodePathName(fnode), parent.getClass(), limited).size(); - } - - @Override - public boolean isLeaf(Object node) { - if (node == mtroot) { - return false; - } - - FieldNode fnode = (FieldNode) node; - Field field = fnode.fieldSet.get(FIELD_INDEX); - if (ReflectionTools.needsIndex(field) && fnode.index == -1) { - return false; - } - boolean r = hasEditor(fnode.obj, field, fnode.index); - return r; - } - - public void vchanged(TreePath path) { - fireTreeNodesChanged(this, path.getPath(), null, null); - } - - @Override - public int getIndexOfChild(Object parent, Object child) { - int cnt = getChildCount(parent); - for (int i = 0; i < cnt; i++) { - if (getChild(parent, i).equals(child)) { - return i; - } - } - return -1; - } - } - - private TreeModel getModel() { - return new MyTreeModel(editedTag); - } - - @Override - public void setEditMode(boolean edit, Tag tag) { - if (tag == null) { - tag = this.tag; - } - this.tag = tag; - SWF swf = tag.getSwf(); - try { - editedTag = tag.cloneTag(); - } catch (InterruptedException ex) { - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - tree.setEditable(edit); - if (!edit) { - tree.stopEditing(); - } - refreshTree(); - } - - @Override - public void save() { - tree.stopEditing(); - SWF swf = tag.getSwf(); - assignTag(tag, editedTag); - tag.setModified(true); - tag.setSwf(swf); - } - - private void assignTag(Tag t, Tag assigned) { - if (t.getClass() != assigned.getClass()) { - return; - } - for (Field f : getAvailableFields(t.getClass())) { - try { - f.set(t, f.get(assigned)); - } catch (IllegalArgumentException | IllegalAccessException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - } - - @Override - public Tag getTag() { - return tag; - } - - public static String swfArrayToString(SWFArray swfArray) { - String result = ""; - if (swfArray == null) { - return result; - } - if (swfArray.count() > 0) { - result += "[" + swfArray.count() + "]"; - } else if (!swfArray.countField().isEmpty()) { - result += "[" + swfArray.countField() + "]"; - } - return result; - } - - public static String swfTypeToString(Class type, SWFType swfType, SWFArray swfArray) { - String stype = type.getSimpleName(); - if (swfType == null) { - return stype + swfArrayToString(swfArray); - } - String result = swfType.value().toString(); - if (swfType.value() == BasicType.OTHER) { - result = stype; - } - if (swfType.count() > 0) { - result += "[" + swfType.count(); - if (swfType.countAdd() > 0) { - result += " + " + swfType.countAdd(); - } - result += "]"; - } else if (!swfType.countField().isEmpty()) { - result += "[" + swfType.countField(); - if (swfType.countAdd() > 0) { - result += " + " + swfType.countAdd(); - } - result += "]"; - } - return result + swfArrayToString(swfArray); - } - - private static boolean hasEditor(Object obj, Field field, int index) { - if (ReflectionTools.needsIndex(field) && index == -1) { - return false; - } - Class type; - try { - Object val = ReflectionTools.getValue(obj, field, index); - if (val == null) { - return false; - } - type = val.getClass(); - } catch (IllegalArgumentException | IllegalAccessException ex) { - return false; - } - SWFType swfType = field.getAnnotation(SWFType.class); - Multiline multiline = field.getAnnotation(Multiline.class); - - if (type.equals(int.class) || type.equals(Integer.class) - || type.equals(short.class) || type.equals(Short.class) - || type.equals(long.class) || type.equals(Long.class) - || type.equals(double.class) || type.equals(Double.class) - || type.equals(float.class) || type.equals(Float.class)) { - return true; - } else if (type.equals(boolean.class) || type.equals(Boolean.class)) { - return true; - } else if (type.equals(String.class)) { - return true; - } else if (type.equals(RGB.class) || type.equals(RGBA.class) || type.equals(ARGB.class)) { - return true; - } else if (type.equals(ByteArrayRange.class)) { - return true; - } else { - return false; - } - } - - private static class FieldSet { - - public List fields; - - public String name; - - public String itemName; - - public FieldSet(Field field) { - fields = new ArrayList<>(); - fields.add(field); - name = field.getName(); - } - - public FieldSet(List fields, String name, String itemName) { - this.fields = fields; - this.name = name; - this.itemName = itemName; - } - - public Field get(int index) { - return fields.get(index); - } - - public int size() { - return fields.size(); - } - } - - private static List
filterFields(MyTreeModel mod, String parentPath, Class cls, boolean limited) { - List
ret = new ArrayList<>(); - List fields = getAvailableFields(cls); - Map> tables = new HashMap<>(); - for (Field f : fields) { - if (limited) { - Conditional cond = f.getAnnotation(Conditional.class); - if (cond != null) { - ConditionEvaluator ev = new ConditionEvaluator(cond); - try { - Map fieldMap = new HashMap<>(); - for (String sf : ev.getFields()) { - String fulldf = parentPath + "." + sf; - FieldNode condnode = (FieldNode) (mod).getNodeByPath(fulldf); - - if (condnode != null) { - Object value = ReflectionTools.getValue(condnode.obj, condnode.fieldSet.get(FIELD_INDEX), condnode.index); - if (value instanceof Boolean) { - fieldMap.put(sf, (Boolean) value); - } else if (value instanceof Integer) { - int intValue = (Integer) value; - boolean found = false; - for (int i : cond.options()) { - if (i == intValue) { - found = true; - } - } - fieldMap.put(sf, found); - } - } else { - fieldMap.put(sf, true); - } - } - if (!ev.eval(fieldMap)) { - continue; - } - } catch (AnnotationParseException | IllegalArgumentException | IllegalAccessException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - } - Table t = f.getAnnotation(Table.class); - List ret1; - if (t != null) { - String tableName = t.value(); - if (!tables.containsKey(tableName)) { - ret1 = new ArrayList<>(); - tables.put(tableName, ret1); - ret.add(new FieldSet(ret1, tableName, t.itemName())); - } - tables.get(tableName).add(f); - } else { - ret.add(new FieldSet(f)); - } - } - return ret; - } - - private static List getAvailableFields(Class cls) { - List ret = fieldCache.get(cls); - if (ret == null) { - ret = new ArrayList<>(); - Field fields[] = cls.getFields(); - for (Field f : fields) { - if (Modifier.isStatic(f.getModifiers())) { - continue; - } - f.setAccessible(true); - Internal inter = f.getAnnotation(Internal.class); - if (inter != null) { - continue; - } - HideInRawEdit hide = f.getAnnotation(HideInRawEdit.class); - if (hide != null) { - continue; - } - ret.add(f); - } - fieldCache.put(cls, ret); - } - return ret; - } - - private void addItem(Object obj, Field field, int index, Class cls) { - SWFArray swfArray = field.getAnnotation(SWFArray.class); - if (swfArray != null && !swfArray.countField().isEmpty()) { //Fields with same countField must be enlarged too - Field fields[] = obj.getClass().getDeclaredFields(); - List sameFlds = new ArrayList<>(); - for (int f = 0; f < fields.length; f++) { - SWFArray fieldSwfArray = fields[f].getAnnotation(SWFArray.class); - if (fieldSwfArray != null && fieldSwfArray.countField().equals(swfArray.countField())) { - sameFlds.add(f); - if (cls == null && !ReflectionTools.canAddToField(obj, fields[f])) { - JOptionPane.showMessageDialog(this, "This field is abstract, cannot be instantiated, sorry."); //TODO!!! - return; - } - - } - } - for (int f : sameFlds) { - ReflectionTools.addToField(obj, fields[f], index, true, cls); - try { - Object v = ReflectionTools.getValue(obj, fields[f], index); - if (v instanceof ASMSource) { - ASMSource asv = (ASMSource) v; - asv.setSourceTag(editedTag); - } - } catch (IllegalArgumentException | IllegalAccessException ex) { - //ignore - } - } - try { - //If countField exists, increment, otherwise do nothing - Field countField = obj.getClass().getDeclaredField(swfArray.countField()); - int cnt = countField.getInt(obj); - cnt++; - countField.setInt(obj, cnt); - } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { - //ignored - } - } else { - if (cls == null && !ReflectionTools.canAddToField(obj, field)) { - JOptionPane.showMessageDialog(this, "This field is abstract, cannot be instantiated, sorry."); //TODO!!! - return; - } - ReflectionTools.addToField(obj, field, index, true, cls); - try { - Object v = ReflectionTools.getValue(obj, field, index); - if (v instanceof ASMSource) { - ASMSource asv = (ASMSource) v; - asv.setSourceTag(editedTag); - } - } catch (IllegalArgumentException | IllegalAccessException ex) { - //ignore - } - } - refreshTree(); - } - - public void refreshTree() { - View.refreshTree(tree, getModel()); - revalidate(); - repaint(); - } - - private void removeItem(Object obj, Field field, int index) { - SWFArray swfArray = field.getAnnotation(SWFArray.class); - if (swfArray != null && !swfArray.countField().isEmpty()) { //Fields with same countField must be removed from too - Field fields[] = obj.getClass().getDeclaredFields(); - for (int f = 0; f < fields.length; f++) { - SWFArray fieldSwfArray = fields[f].getAnnotation(SWFArray.class); - if (fieldSwfArray != null && fieldSwfArray.countField().equals(swfArray.countField())) { - ReflectionTools.removeFromField(obj, fields[f], index); - } - } - try { - //If countField exists, decrement, otherwise do nothing - Field countField = obj.getClass().getDeclaredField(swfArray.countField()); - int cnt = countField.getInt(obj); - cnt--; - countField.setInt(obj, cnt); - } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { - //ignored - } - } else { - ReflectionTools.removeFromField(obj, field, index); - } - - refreshTree(); - } -} +/* + * Copyright (C) 2010-2015 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.gui.generictageditors.BooleanEditor; +import com.jpexs.decompiler.flash.gui.generictageditors.ColorEditor; +import com.jpexs.decompiler.flash.gui.generictageditors.GenericTagEditor; +import com.jpexs.decompiler.flash.gui.generictageditors.NumberEditor; +import com.jpexs.decompiler.flash.gui.generictageditors.StringEditor; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.types.ARGB; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.HideInRawEdit; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.Multiline; +import com.jpexs.decompiler.flash.types.annotations.SWFArray; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.decompiler.flash.types.annotations.Table; +import com.jpexs.decompiler.flash.types.annotations.parser.AnnotationParseException; +import com.jpexs.decompiler.flash.types.annotations.parser.ConditionEvaluator; +import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.ConcreteClasses; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.ReflectionTools; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +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.io.File; +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.EventObject; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.AbstractCellEditor; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JTree; +import javax.swing.event.TreeModelListener; +import javax.swing.plaf.basic.BasicLabelUI; +import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeCellEditor; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +/** + * + * @author JPEXS + */ +public class GenericTagTreePanel extends GenericTagPanel { + + private static final Logger logger = Logger.getLogger(GenericTagTreePanel.class.getName()); + + private final MainPanel mainPanel; + + private JTree tree; + + private Tag editedTag; + + private static final Map> fieldCache = new HashMap<>(); + + private static final int FIELD_INDEX = 0; + + private class MyTree extends JTree { + + public MyTree() { + setBackground(Color.white); + setUI(new BasicTreeUI() { + + @Override + public void paint(Graphics g, JComponent c) { + setHashColor(Color.gray); + super.paint(g, c); + } + + }); + setCellRenderer(new MyTreeCellRenderer()); + setCellEditor(new MyTreeCellEditor(this)); + setInvokesStopCellEditing(true); + + } + } + + private class MyTreeCellEditor extends AbstractCellEditor implements TreeCellEditor { + + private List editors = null; + + private final JTree tree; + + private FieldNode fnode; + + public MyTreeCellEditor(JTree tree) { + this.tree = tree; + } + + @Override + public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { + if (value instanceof FieldNode) { + fnode = (FieldNode) value; + JPanel panSum = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + panSum.setOpaque(false); + for (int i = 0; i < fnode.fieldSet.size(); i++) { + Field field = fnode.fieldSet.get(i);//fnode.fieldSet.get(FIELD_INDEX); + int index = fnode.index; + Object obj = fnode.obj; + Class type; + try { + type = ReflectionTools.getValue(obj, field, index).getClass(); + } catch (IllegalArgumentException | IllegalAccessException ex) { + logger.log(Level.SEVERE, "Fixing characters order failed, recursion detected."); + return null; + } + GenericTagEditor editor = null; + SWFType swfType = field.getAnnotation(SWFType.class); + Multiline multiline = field.getAnnotation(Multiline.class); + if (type.equals(int.class) || type.equals(Integer.class) + || type.equals(short.class) || type.equals(Short.class) + || type.equals(long.class) || type.equals(Long.class) + || type.equals(double.class) || type.equals(Double.class) + || type.equals(float.class) || type.equals(Float.class)) { + editor = new NumberEditor(field.getName(), obj, field, index, type, swfType); + } else if (type.equals(boolean.class) || type.equals(Boolean.class)) { + editor = new BooleanEditor(field.getName(), obj, field, index, type); + } else if (type.equals(String.class)) { + editor = new StringEditor(field.getName(), obj, field, index, type, multiline != null); + } else if (type.equals(RGB.class) || type.equals(RGBA.class) || type.equals(ARGB.class)) { + editor = new ColorEditor(field.getName(), obj, field, index, type); + } + if (editor != null) { + if (editors == null) { + editors = new ArrayList<>(); + } + editors.add(editor); + } + JPanel pan = new JPanel(); + FlowLayout fl = new FlowLayout(FlowLayout.LEFT, 0, 0); + fl.setAlignOnBaseline(true); + pan.setLayout(fl); + JLabel nameLabel = new JLabel(fnode.getNameType(i) + " = ") { + + @Override + public BaselineResizeBehavior getBaselineResizeBehavior() { + return Component.BaselineResizeBehavior.CONSTANT_ASCENT; + } + + @Override + public int getBaseline(int width, int height) { + return 0; + } + + }; + pan.setBackground(Color.white); + nameLabel.setAlignmentY(TOP_ALIGNMENT); + pan.add(nameLabel); + + JComponent editorComponent = (JComponent) editor; + if (editorComponent == null && type.equals(ByteArrayRange.class)) { + //ByteArrayRange bar = (ByteArrayRange) field.get(obj); + nameLabel.setText(nameLabel.getText() + " ..."); + } + if (editorComponent != null) { + nameLabel.setSize(nameLabel.getWidth(), editorComponent.getHeight()); + editorComponent.setAlignmentY(TOP_ALIGNMENT); + pan.add(editorComponent); + pan.setPreferredSize(new Dimension((int) nameLabel.getPreferredSize().getWidth() + 5 + (int) editorComponent.getPreferredSize().getWidth(), (int) editorComponent.getPreferredSize().getHeight())); + } else { + pan.setPreferredSize(new Dimension((int) nameLabel.getPreferredSize().getWidth(), (int) nameLabel.getPreferredSize().getHeight())); + } + panSum.add(pan); + } + return panSum; + } + return null; + + } + + @Override + public Object getCellEditorValue() { + List ret = new ArrayList<>(); + if (editors != null) { + for (GenericTagEditor editor : editors) { + ret.add(editor.getChangedValue()); + } + } + return ret; + } + + @Override + public boolean isCellEditable(EventObject e) { + if (!(e instanceof MouseEvent)) { + return false; + } + + MouseEvent me = (MouseEvent) e; + TreePath path = tree.getPathForLocation(me.getX(), me.getY()); + + if (path == null) { + return false; + } + Object obj = path.getLastPathComponent(); + + boolean ret = super.isCellEditable(e) + && tree.getModel().isLeaf(obj); + return ret; + } + + @Override + public boolean stopCellEditing() { + super.stopCellEditing(); + + /*List depends = ((MyTreeModel) tree.getModel()).getDependentFields(fnode); + boolean dep = false; + if (!depends.isEmpty()) { + dep = true; + } */ + if (editors != null) { + for (GenericTagEditor editor : editors) { + editor.save(); + } + } + TreePath sp = tree.getSelectionPath(); + if (sp != null) { + ((MyTreeModel) tree.getModel()).vchanged(sp); + } + refreshTree(); + return true; + } + } + + public GenericTagTreePanel(MainPanel mainPanel) { + setLayout(new BorderLayout()); + this.mainPanel = mainPanel; + tree = new MyTree(); + + add(new JScrollPane(tree), BorderLayout.CENTER); + tree.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (!tree.isEditable()) { + return; + } + int selRow = tree.getRowForLocation(e.getX(), e.getY()); + TreePath selPath = tree.getPathForLocation(e.getX(), e.getY()); + if (selRow != -1 && selPath != null) { + if (e.getClickCount() == 1) { + if (e.getButton() == MouseEvent.BUTTON3) { //right click + Object selObject = selPath.getLastPathComponent(); + if (selObject instanceof FieldNode) { + final FieldNode fnode = (FieldNode) selObject; + Field field = fnode.fieldSet.get(FIELD_INDEX); + if (field.getType().equals(ByteArrayRange.class)) { + File selectedFile = mainPanel.showImportFileChooser(""); + if (selectedFile != null) { + File selfile = Helper.fixDialogFile(selectedFile); + byte[] data = Helper.readFile(selfile.getAbsolutePath()); + try { + field.set(fnode.obj, new ByteArrayRange(data)); + } catch (IllegalArgumentException | IllegalAccessException ex) { + Logger.getLogger(GenericTagTreePanel.class.getName()).log(Level.SEVERE, null, ex); + } + } + } else if (ReflectionTools.needsIndex(field)) { + SWFArray swfArray = fnode.fieldSet.get(FIELD_INDEX).getAnnotation(SWFArray.class); + + String itemStr = ""; + if (swfArray != null) { + itemStr = swfArray.value(); + } + if (fnode.fieldSet.itemName != null && !fnode.fieldSet.itemName.isEmpty()) { + itemStr = fnode.fieldSet.itemName; + } + if (itemStr.isEmpty()) { + itemStr = AppStrings.translate("generictag.array.item"); + } + + boolean canAdd = true; + if (!ReflectionTools.canAddToField(fnode.obj, fnode.fieldSet.get(FIELD_INDEX))) { + canAdd = false; + } + JPopupMenu p = new JPopupMenu(); + JMenuItem mi; + Class subtype = ReflectionTools.getFieldSubType(fnode.obj, fnode.fieldSet.get(FIELD_INDEX)); + if (!canAdd && subtype.isAnnotationPresent(ConcreteClasses.class)) { + Class[] availableClasses = subtype.getAnnotation(ConcreteClasses.class).value(); + JMenu mBegin = new JMenu(AppStrings.translate("generictag.array.insertbeginning").replace("%item%", itemStr)); + p.add(mBegin); + JMenu mBefore = new JMenu(AppStrings.translate("generictag.array.insertbefore").replace("%item%", itemStr)); + p.add(mBefore); + mi = new JMenuItem(AppStrings.translate("generictag.array.remove").replace("%item%", itemStr)); + mi.addActionListener((ActionEvent e1) -> { + removeItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), fnode.index); + }); + p.add(mi); + JMenu mAfter = new JMenu(AppStrings.translate("generictag.array.insertafter").replace("%item%", itemStr)); + p.add(mAfter); + + JMenu mEnd = new JMenu(AppStrings.translate("generictag.array.insertend").replace("%item%", itemStr)); + p.add(mEnd); + + for (Class c : availableClasses) { + mi = new JMenuItem(c.getSimpleName()); + mi.addActionListener((ActionEvent e1) -> { + addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), 0, c); + }); + mBegin.add(mi); + + mi = new JMenuItem(c.getSimpleName()); + mi.addActionListener((ActionEvent e1) -> { + addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), fnode.index, c); + }); + mBefore.add(mi); + + mi = new JMenuItem(c.getSimpleName()); + mi.addActionListener((ActionEvent e1) -> { + addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), fnode.index + 1, c); + }); + mAfter.add(mi); + + mi = new JMenuItem(c.getSimpleName()); + mi.addActionListener((ActionEvent e1) -> { + addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), ReflectionTools.getFieldSubSize(fnode.obj, fnode.fieldSet.get(FIELD_INDEX)), c); + }); + mEnd.add(mi); + } + } else { + + mi = new JMenuItem(AppStrings.translate("generictag.array.insertbeginning").replace("%item%", itemStr)); + mi.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), 0, null); + } + }); + if (!canAdd) { + mi.setEnabled(false); + } + p.add(mi); + + if (fnode.index > -1) { + mi = new JMenuItem(AppStrings.translate("generictag.array.insertbefore").replace("%item%", itemStr)); + mi.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), fnode.index, null); + } + }); + if (!canAdd) { + mi.setEnabled(false); + } + p.add(mi); + + mi = new JMenuItem(AppStrings.translate("generictag.array.remove").replace("%item%", itemStr)); + mi.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + removeItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), fnode.index); + } + }); + p.add(mi); + + mi = new JMenuItem(AppStrings.translate("generictag.array.insertafter").replace("%item%", itemStr)); + mi.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), fnode.index + 1, null); + } + }); + if (!canAdd) { + mi.setEnabled(false); + } + p.add(mi); + } + + mi = new JMenuItem(AppStrings.translate("generictag.array.insertend").replace("%item%", itemStr)); + mi.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + addItem(fnode.obj, fnode.fieldSet.get(FIELD_INDEX), ReflectionTools.getFieldSubSize(fnode.obj, fnode.fieldSet.get(FIELD_INDEX)), null); + } + }); + if (!canAdd) { + mi.setEnabled(false); + } + p.add(mi); + //} + } + p.show(tree, e.getX(), e.getY()); + } + } + } + //} else if (e.getClickCount() == 2) { + // myDoubleClick(selRow, selPath); + } + } + } + }); + } + + private Tag tag; + + public class MyTreeCellRenderer extends DefaultTreeCellRenderer { + + public MyTreeCellRenderer() { + setUI(new BasicLabelUI()); + setOpaque(false); + setBackgroundNonSelectionColor(Color.white); + } + } + + @Override + public void clear() { + + } + + private static final class TableFieldNodes extends DefaultMutableTreeNode { + + List subnodes; + + public TableFieldNodes(List subnodes) { + this.subnodes = subnodes; + } + } + + private static final class FieldNode extends DefaultMutableTreeNode { + + private Object obj; + + private FieldSet fieldSet; + + private int index; + + public FieldNode(Object obj, FieldSet fieldSet, int index) { + this.obj = obj; + this.fieldSet = fieldSet; + this.index = index; + + for (int i = 0; i < fieldSet.size(); i++) { + if (getValue(i) == null) { + try { + if (List.class.isAssignableFrom(fieldSet.get(i).getType())) { + ReflectionTools.setValue(obj, fieldSet.get(i), new ArrayList<>()); + } else if (fieldSet.get(i).getType().isArray()) { + ReflectionTools.setValue(obj, fieldSet.get(i), Array.newInstance(fieldSet.get(i).getType().getComponentType(), 0)); + } + } catch (IllegalArgumentException | IllegalAccessException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + } + + @Override + public void setUserObject(Object userObject) { + + } + + /* + */ + @Override + public String toString() { + String ret = ""; + if (index > -1) { + for (int i = 0; i < fieldSet.size(); i++) { + if (i > 0) { + ret += ", "; + } + ret += toString(i); + } + return ret; + } + + if (fieldSet.size() == 1) { + ret = toString(0); + } else { + ret = fieldSet.name; + SWFArray t = fieldSet.get(0).getAnnotation(SWFArray.class); + if (t != null) { + ret += " [" + t.countField() + "]"; + } else { + ret += " []"; + } + } + + return "" + ret + ""; + } + + public String toString(int fieldIndex) { + String valStr = ""; + if (ReflectionTools.needsIndex(fieldSet.get(fieldIndex)) && (index == -1)) { + valStr += ""; + } else if (hasEditor(obj, fieldSet.get(fieldIndex), index)) { + Object val = getValue(fieldIndex); + Color color = null; + String colorAdd = ""; + if (val instanceof RGB) { //Note: Can be RGBA too + color = ((RGB) val).toColor(); + } + if (val instanceof ARGB) { + color = ((ARGB) val).toColor(); + } + + if (color != null) { + colorAdd = "\u25cf "; + } + + if (val instanceof ByteArrayRange) { + valStr += " = " + ((ByteArrayRange) val).getLength() + " byte"; + } else { + valStr += " = " + colorAdd + val.toString(); + } + } + return getNameType(fieldIndex) + valStr; + } + + public String getType(int fieldIndex) { + SWFType swfType = fieldSet.get(fieldIndex).getAnnotation(SWFType.class); + SWFArray swfArray = fieldSet.get(fieldIndex).getAnnotation(SWFArray.class); + String typeStr = null; + if ((swfType != null || swfArray != null) && !(ReflectionTools.needsIndex(fieldSet.get(fieldIndex)) && (index > -1))) { + Class type = fieldSet.get(fieldIndex).getType(); + if (ReflectionTools.needsIndex(fieldSet.get(fieldIndex))) { + type = ReflectionTools.getFieldSubType(obj, fieldSet.get(fieldIndex)); + } + typeStr = swfTypeToString(type, swfType, swfArray); + } + return typeStr; + } + + public String getNameType(int fieldIndex) { + String typeStr = getType(fieldIndex); + return getName(fieldIndex) + (typeStr != null ? " : " + typeStr : ""); + } + + public String getName(int fieldIndex) { + SWFArray swfArray = fieldSet.get(fieldIndex).getAnnotation(SWFArray.class); + String name = ""; + if (swfArray != null) { + name = swfArray.value(); + } + + Object val = null; + try { + if (index > -1) { + val = ReflectionTools.getValue(obj, fieldSet.get(fieldIndex), index); + } + } catch (IllegalArgumentException | IllegalAccessException ex) { + //ignore + } + String typeAdd = ""; + if (val != null) { + typeAdd = " : " + val.getClass().getSimpleName(); + } + + return (index > -1 ? name + "[" + index + "]" + typeAdd : fieldSet.get(fieldIndex).getName()); + } + + public Object getValue(int fieldIndex) { + try { + if (ReflectionTools.needsIndex(fieldSet.get(fieldIndex)) && (index == -1)) { + return ReflectionTools.getValue(obj, fieldSet.get(fieldIndex)); + } + Object val = ReflectionTools.getValue(obj, fieldSet.get(fieldIndex), index); + if (val == null) { + try { + val = ReflectionTools.newInstanceOf(fieldSet.get(fieldIndex).getType()); + ReflectionTools.setValue(obj, fieldSet.get(fieldIndex), index, val); + } catch (InstantiationException | IllegalAccessException ex) { + logger.log(Level.SEVERE, null, ex); + return null; + } + } + return val; + } catch (IllegalArgumentException | IllegalAccessException ex) { + return null; + } + } + + @Override + public int hashCode() { + int hash = 7; + hash = 11 * hash + Objects.hashCode(this.obj); + hash = 11 * hash + Objects.hashCode(this.fieldSet.get(FIELD_INDEX)); + hash = 11 * hash + this.index; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final FieldNode other = (FieldNode) obj; + if (!Objects.equals(this.obj, other.obj)) { + return false; + } + if (!Objects.equals(this.fieldSet.get(FIELD_INDEX), other.fieldSet.get(FIELD_INDEX))) { + return false; + } + return this.index == other.index; + } + } + + private static class MyTreeModel extends DefaultTreeModel { + + private final Object mtroot; + + private final List listeners = new ArrayList<>(); + + private final Map nodeCache = new HashMap<>(); + + // it is much faster to store the reverse mappings, too + private final Map nodeCacheReverse = new HashMap<>(); + + private Object getNodeByPath(String path) { + + if (nodeCache.containsKey(path)) { + return nodeCache.get(path); + } + return null; + } + + public String getNodePathName(Object find) { + + if (nodeCacheReverse.containsKey(find)) { + return nodeCacheReverse.get(find); + } + return null; + } + + public List getDependentFields(FieldNode fnode) { + List ret = new ArrayList<>(); + getDependentFields(getNodePathName(fnode), mtroot.getClass().getSimpleName(), mtroot, ret); + return ret; + } + + public void getDependentFields(String dependence, String currentPath, Object node, List ret) { + if (node instanceof FieldNode) { + FieldNode fnode = (FieldNode) node; + Conditional cond = fnode.fieldSet.get(FIELD_INDEX).getAnnotation(Conditional.class); + if (cond != null) { + ConditionEvaluator ev = new ConditionEvaluator(cond); + String parentPath = currentPath.indexOf('.') == -1 ? "" : currentPath.substring(0, currentPath.lastIndexOf('.')); + try { + for (String cname : ev.getFields()) { + String fullParh = parentPath + "." + cname; + if (fullParh.equals(dependence)) { + ret.add(fnode); + break; + } + } + } catch (AnnotationParseException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + int count = getChildCount(node); + for (int i = 0; i < count; i++) { + FieldNode f = (FieldNode) getChild(node, i); + getDependentFields(dependence, currentPath + "." + f.getName(FIELD_INDEX), f, ret); + } + } + + public MyTreeModel(Tag root) { + super(new DefaultMutableTreeNode(root)); + this.mtroot = root; + buildCache(root, ""); + } + + private void buildCache(Object obj, String parentPath) { + if (!"".equals(parentPath)) { + parentPath += "."; + } + if (obj instanceof FieldNode) { + FieldNode fn = (FieldNode) obj; + parentPath += fn.getName(FIELD_INDEX); + } else { + parentPath += obj.getClass().getSimpleName(); + } + nodeCache.put(parentPath, obj); + nodeCacheReverse.put(obj, parentPath); + int count = getChildCount(obj, false); + for (int i = 0; i < count; i++) { + buildCache(getChild(obj, i, false), parentPath); + } + } + + @Override + public Object getRoot() { + return mtroot; + } + + private Object getChild(Object parent, int index, boolean limited) { + if (parent == mtroot) { + return new FieldNode(mtroot, filterFields(this, mtroot.getClass().getSimpleName(), mtroot.getClass(), limited).get(index), -1); + } + FieldNode fnode = (FieldNode) parent; + Field field = fnode.fieldSet.get(FIELD_INDEX); + if (ReflectionTools.needsIndex(field) && (fnode.index == -1)) { //Arrays ot Lists + return new FieldNode(fnode.obj, fnode.fieldSet, index); + } + parent = fnode.getValue(FIELD_INDEX); + return new FieldNode(parent, filterFields(this, getNodePathName(fnode), parent.getClass(), limited).get(index), -1); + } + + @Override + public Object getChild(Object parent, int index) { + return getChild(parent, index, true); + } + + @Override + public int getChildCount(Object parent) { + return getChildCount(parent, true); + } + + private int getChildCount(Object parent, boolean limited) { + if (parent == mtroot) { + return filterFields(this, mtroot.getClass().getSimpleName(), mtroot.getClass(), limited).size(); + } + FieldNode fnode = (FieldNode) parent; + if (isLeaf(fnode)) { + return 0; + } + Field field = fnode.fieldSet.get(FIELD_INDEX); + if (ReflectionTools.needsIndex(field) && (fnode.index == -1)) { //Arrays or Lists + try { + if (field.get(fnode.obj) == null) { + // todo: instanciate the (Array)List or Array to allow adding items to it + return 0; + } + } catch (IllegalArgumentException | IllegalAccessException ex) { + return 0; + } + + return ReflectionTools.getFieldSubSize(fnode.obj, field); + } + parent = fnode.getValue(FIELD_INDEX); + + return filterFields(this, getNodePathName(fnode), parent.getClass(), limited).size(); + } + + @Override + public boolean isLeaf(Object node) { + if (node == mtroot) { + return false; + } + + FieldNode fnode = (FieldNode) node; + Field field = fnode.fieldSet.get(FIELD_INDEX); + if (ReflectionTools.needsIndex(field) && fnode.index == -1) { + return false; + } + boolean r = hasEditor(fnode.obj, field, fnode.index); + return r; + } + + public void vchanged(TreePath path) { + fireTreeNodesChanged(this, path.getPath(), null, null); + } + + @Override + public int getIndexOfChild(Object parent, Object child) { + int cnt = getChildCount(parent); + for (int i = 0; i < cnt; i++) { + if (getChild(parent, i).equals(child)) { + return i; + } + } + return -1; + } + } + + private TreeModel getModel() { + return new MyTreeModel(editedTag); + } + + @Override + public void setEditMode(boolean edit, Tag tag) { + if (tag == null) { + tag = this.tag; + } + this.tag = tag; + SWF swf = tag.getSwf(); + try { + editedTag = tag.cloneTag(); + } catch (InterruptedException ex) { + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + tree.setEditable(edit); + if (!edit) { + tree.stopEditing(); + } + refreshTree(); + } + + @Override + public void save() { + tree.stopEditing(); + SWF swf = tag.getSwf(); + assignTag(tag, editedTag); + tag.setModified(true); + tag.setSwf(swf); + } + + private void assignTag(Tag t, Tag assigned) { + if (t.getClass() != assigned.getClass()) { + return; + } + for (Field f : getAvailableFields(t.getClass())) { + try { + f.set(t, f.get(assigned)); + } catch (IllegalArgumentException | IllegalAccessException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + + @Override + public Tag getTag() { + return tag; + } + + public static String swfArrayToString(SWFArray swfArray) { + String result = ""; + if (swfArray == null) { + return result; + } + if (swfArray.count() > 0) { + result += "[" + swfArray.count() + "]"; + } else if (!swfArray.countField().isEmpty()) { + result += "[" + swfArray.countField() + "]"; + } + return result; + } + + public static String swfTypeToString(Class type, SWFType swfType, SWFArray swfArray) { + String stype = type.getSimpleName(); + if (swfType == null) { + return stype + swfArrayToString(swfArray); + } + String result = swfType.value().toString(); + if (swfType.value() == BasicType.OTHER) { + result = stype; + } + if (swfType.count() > 0) { + result += "[" + swfType.count(); + if (swfType.countAdd() > 0) { + result += " + " + swfType.countAdd(); + } + result += "]"; + } else if (!swfType.countField().isEmpty()) { + result += "[" + swfType.countField(); + if (swfType.countAdd() > 0) { + result += " + " + swfType.countAdd(); + } + result += "]"; + } + return result + swfArrayToString(swfArray); + } + + private static boolean hasEditor(Object obj, Field field, int index) { + if (ReflectionTools.needsIndex(field) && index == -1) { + return false; + } + Class type; + try { + Object val = ReflectionTools.getValue(obj, field, index); + if (val == null) { + return false; + } + type = val.getClass(); + } catch (IllegalArgumentException | IllegalAccessException ex) { + return false; + } + SWFType swfType = field.getAnnotation(SWFType.class); + Multiline multiline = field.getAnnotation(Multiline.class); + + if (type.equals(int.class) || type.equals(Integer.class) + || type.equals(short.class) || type.equals(Short.class) + || type.equals(long.class) || type.equals(Long.class) + || type.equals(double.class) || type.equals(Double.class) + || type.equals(float.class) || type.equals(Float.class)) { + return true; + } else if (type.equals(boolean.class) || type.equals(Boolean.class)) { + return true; + } else if (type.equals(String.class)) { + return true; + } else if (type.equals(RGB.class) || type.equals(RGBA.class) || type.equals(ARGB.class)) { + return true; + } else if (type.equals(ByteArrayRange.class)) { + return true; + } else { + return false; + } + } + + private static class FieldSet { + + public List fields; + + public String name; + + public String itemName; + + public FieldSet(Field field) { + fields = new ArrayList<>(); + fields.add(field); + name = field.getName(); + } + + public FieldSet(List fields, String name, String itemName) { + this.fields = fields; + this.name = name; + this.itemName = itemName; + } + + public Field get(int index) { + return fields.get(index); + } + + public int size() { + return fields.size(); + } + } + + private static List
filterFields(MyTreeModel mod, String parentPath, Class cls, boolean limited) { + List
ret = new ArrayList<>(); + List fields = getAvailableFields(cls); + Map> tables = new HashMap<>(); + for (Field f : fields) { + if (limited) { + Conditional cond = f.getAnnotation(Conditional.class); + if (cond != null) { + ConditionEvaluator ev = new ConditionEvaluator(cond); + try { + Map fieldMap = new HashMap<>(); + for (String sf : ev.getFields()) { + String fulldf = parentPath + "." + sf; + FieldNode condnode = (FieldNode) (mod).getNodeByPath(fulldf); + + if (condnode != null) { + Object value = ReflectionTools.getValue(condnode.obj, condnode.fieldSet.get(FIELD_INDEX), condnode.index); + if (value instanceof Boolean) { + fieldMap.put(sf, (Boolean) value); + } else if (value instanceof Integer) { + int intValue = (Integer) value; + boolean found = false; + for (int i : cond.options()) { + if (i == intValue) { + found = true; + } + } + fieldMap.put(sf, found); + } + } else { + fieldMap.put(sf, true); + } + } + if (!ev.eval(fieldMap)) { + continue; + } + } catch (AnnotationParseException | IllegalArgumentException | IllegalAccessException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + Table t = f.getAnnotation(Table.class); + List ret1; + if (t != null) { + String tableName = t.value(); + if (!tables.containsKey(tableName)) { + ret1 = new ArrayList<>(); + tables.put(tableName, ret1); + ret.add(new FieldSet(ret1, tableName, t.itemName())); + } + tables.get(tableName).add(f); + } else { + ret.add(new FieldSet(f)); + } + } + return ret; + } + + private static List getAvailableFields(Class cls) { + List ret = fieldCache.get(cls); + if (ret == null) { + ret = new ArrayList<>(); + Field fields[] = cls.getFields(); + for (Field f : fields) { + if (Modifier.isStatic(f.getModifiers())) { + continue; + } + f.setAccessible(true); + Internal inter = f.getAnnotation(Internal.class); + if (inter != null) { + continue; + } + HideInRawEdit hide = f.getAnnotation(HideInRawEdit.class); + if (hide != null) { + continue; + } + ret.add(f); + } + fieldCache.put(cls, ret); + } + return ret; + } + + private void addItem(Object obj, Field field, int index, Class cls) { + SWFArray swfArray = field.getAnnotation(SWFArray.class); + if (swfArray != null && !swfArray.countField().isEmpty()) { //Fields with same countField must be enlarged too + Field fields[] = obj.getClass().getDeclaredFields(); + List sameFlds = new ArrayList<>(); + for (int f = 0; f < fields.length; f++) { + SWFArray fieldSwfArray = fields[f].getAnnotation(SWFArray.class); + if (fieldSwfArray != null && fieldSwfArray.countField().equals(swfArray.countField())) { + sameFlds.add(f); + if (cls == null && !ReflectionTools.canAddToField(obj, fields[f])) { + JOptionPane.showMessageDialog(this, "This field is abstract, cannot be instantiated, sorry."); //TODO!!! + return; + } + + } + } + for (int f : sameFlds) { + ReflectionTools.addToField(obj, fields[f], index, true, cls); + try { + Object v = ReflectionTools.getValue(obj, fields[f], index); + if (v instanceof ASMSource) { + ASMSource asv = (ASMSource) v; + asv.setSourceTag(editedTag); + } + } catch (IllegalArgumentException | IllegalAccessException ex) { + //ignore + } + } + try { + //If countField exists, increment, otherwise do nothing + Field countField = obj.getClass().getDeclaredField(swfArray.countField()); + int cnt = countField.getInt(obj); + cnt++; + countField.setInt(obj, cnt); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { + //ignored + } + } else { + if (cls == null && !ReflectionTools.canAddToField(obj, field)) { + JOptionPane.showMessageDialog(this, "This field is abstract, cannot be instantiated, sorry."); //TODO!!! + return; + } + ReflectionTools.addToField(obj, field, index, true, cls); + try { + Object v = ReflectionTools.getValue(obj, field, index); + if (v instanceof ASMSource) { + ASMSource asv = (ASMSource) v; + asv.setSourceTag(editedTag); + } + } catch (IllegalArgumentException | IllegalAccessException ex) { + //ignore + } + } + refreshTree(); + } + + public void refreshTree() { + View.refreshTree(tree, getModel()); + revalidate(); + repaint(); + } + + private void removeItem(Object obj, Field field, int index) { + SWFArray swfArray = field.getAnnotation(SWFArray.class); + if (swfArray != null && !swfArray.countField().isEmpty()) { //Fields with same countField must be removed from too + Field fields[] = obj.getClass().getDeclaredFields(); + for (int f = 0; f < fields.length; f++) { + SWFArray fieldSwfArray = fields[f].getAnnotation(SWFArray.class); + if (fieldSwfArray != null && fieldSwfArray.countField().equals(swfArray.countField())) { + ReflectionTools.removeFromField(obj, fields[f], index); + } + } + try { + //If countField exists, decrement, otherwise do nothing + Field countField = obj.getClass().getDeclaredField(swfArray.countField()); + int cnt = countField.getInt(obj); + cnt--; + countField.setInt(obj, cnt); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { + //ignored + } + } else { + ReflectionTools.removeFromField(obj, field, index); + } + + refreshTree(); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index db2205126..0a9a62c0d 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -1,3369 +1,3369 @@ -/* - * Copyright (C) 2010-2015 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.AbortRetryIgnoreHandler; -import com.jpexs.decompiler.flash.ApplicationInfo; -import com.jpexs.decompiler.flash.EventListener; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFBundle; -import com.jpexs.decompiler.flash.SWFSourceInfo; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.RenameType; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.DeobfuscationLevel; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.configuration.ConfigurationItem; -import com.jpexs.decompiler.flash.configuration.SwfSpecificConfiguration; -import com.jpexs.decompiler.flash.dumpview.DumpInfo; -import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode; -import com.jpexs.decompiler.flash.exporters.BinaryDataExporter; -import com.jpexs.decompiler.flash.exporters.FontExporter; -import com.jpexs.decompiler.flash.exporters.FrameExporter; -import com.jpexs.decompiler.flash.exporters.ImageExporter; -import com.jpexs.decompiler.flash.exporters.MorphShapeExporter; -import com.jpexs.decompiler.flash.exporters.MovieExporter; -import com.jpexs.decompiler.flash.exporters.ShapeExporter; -import com.jpexs.decompiler.flash.exporters.SoundExporter; -import com.jpexs.decompiler.flash.exporters.SymbolClassExporter; -import com.jpexs.decompiler.flash.exporters.TextExporter; -import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ButtonExportMode; -import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; -import com.jpexs.decompiler.flash.exporters.modes.FrameExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode; -import com.jpexs.decompiler.flash.exporters.modes.MorphShapeExportMode; -import com.jpexs.decompiler.flash.exporters.modes.MovieExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode; -import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode; -import com.jpexs.decompiler.flash.exporters.modes.SpriteExportMode; -import com.jpexs.decompiler.flash.exporters.modes.SymbolClassExportMode; -import com.jpexs.decompiler.flash.exporters.modes.TextExportMode; -import com.jpexs.decompiler.flash.exporters.script.AS2ScriptExporter; -import com.jpexs.decompiler.flash.exporters.script.AS3ScriptExporter; -import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.ButtonExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.FrameExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.ImageExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.MorphShapeExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.MovieExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.ShapeExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.SpriteExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings; -import com.jpexs.decompiler.flash.exporters.swf.SwfJavaExporter; -import com.jpexs.decompiler.flash.exporters.swf.SwfXmlExporter; -import com.jpexs.decompiler.flash.gui.abc.ABCPanel; -import com.jpexs.decompiler.flash.gui.abc.ABCPanelSearchResult; -import com.jpexs.decompiler.flash.gui.abc.ClassesListTreeModel; -import com.jpexs.decompiler.flash.gui.abc.DecompiledEditorPane; -import com.jpexs.decompiler.flash.gui.abc.DeobfuscationDialog; -import com.jpexs.decompiler.flash.gui.action.ActionPanel; -import com.jpexs.decompiler.flash.gui.action.ActionSearchResult; -import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; -import com.jpexs.decompiler.flash.gui.dumpview.DumpTree; -import com.jpexs.decompiler.flash.gui.dumpview.DumpTreeModel; -import com.jpexs.decompiler.flash.gui.dumpview.DumpViewPanel; -import com.jpexs.decompiler.flash.gui.helpers.ObservableList; -import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; -import com.jpexs.decompiler.flash.gui.tagtree.TagTree; -import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; -import com.jpexs.decompiler.flash.gui.timeline.TimelineViewPanel; -import com.jpexs.decompiler.flash.helpers.FileTextWriter; -import com.jpexs.decompiler.flash.helpers.Freed; -import com.jpexs.decompiler.flash.importers.AS2ScriptImporter; -import com.jpexs.decompiler.flash.importers.AS3ScriptImporter; -import com.jpexs.decompiler.flash.importers.BinaryDataImporter; -import com.jpexs.decompiler.flash.importers.ImageImporter; -import com.jpexs.decompiler.flash.importers.ShapeImporter; -import com.jpexs.decompiler.flash.importers.SwfXmlImporter; -import com.jpexs.decompiler.flash.importers.SymbolClassImporter; -import com.jpexs.decompiler.flash.importers.TextImporter; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; -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.FileAttributesTag; -import com.jpexs.decompiler.flash.tags.MetadataTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.TagInfo; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.ButtonTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.DrawableTag; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; -import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; -import com.jpexs.decompiler.flash.tags.base.SoundTag; -import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag; -import com.jpexs.decompiler.flash.tags.base.TextImportErrorHandler; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.text.TextAlign; -import com.jpexs.decompiler.flash.tags.text.TextParseException; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.timeline.TagScript; -import com.jpexs.decompiler.flash.timeline.Timeline; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.treeitems.FolderItem; -import com.jpexs.decompiler.flash.treeitems.HeaderItem; -import com.jpexs.decompiler.flash.treeitems.SWFList; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.sound.SoundFormat; -import com.jpexs.decompiler.flash.xfl.FLAVersion; -import com.jpexs.helpers.CancellableWorker; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.Path; -import com.jpexs.helpers.ProgressListener; -import com.jpexs.helpers.SerializableImage; -import java.awt.BorderLayout; -import java.awt.CardLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Container; -import java.awt.Desktop; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.datatransfer.UnsupportedFlavorException; -import java.awt.dnd.DnDConstants; -import java.awt.dnd.DragGestureEvent; -import java.awt.dnd.DragGestureListener; -import java.awt.dnd.DragSource; -import java.awt.dnd.DragSourceDragEvent; -import java.awt.dnd.DragSourceDropEvent; -import java.awt.dnd.DragSourceEvent; -import java.awt.dnd.DragSourceListener; -import java.awt.dnd.DropTarget; -import java.awt.dnd.DropTargetDropEvent; -import java.awt.event.ActionEvent; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.CancellationException; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.sound.sampled.LineUnavailableException; -import javax.sound.sampled.UnsupportedAudioFileException; -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.Icon; -import javax.swing.JColorChooser; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTabbedPane; -import javax.swing.JTextField; -import javax.swing.SwingConstants; -import javax.swing.UIManager; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; -import javax.swing.filechooser.FileFilter; -import javax.swing.plaf.basic.BasicTreeUI; -import javax.swing.tree.DefaultTreeSelectionModel; -import javax.swing.tree.TreePath; -import jsyntaxpane.DefaultSyntaxKit; - -/** - * - * @author JPEXS - */ -public final class MainPanel extends JPanel implements TreeSelectionListener, SearchListener, Freed { - - private final MainFrame mainFrame; - - private final ObservableList swfs; - - private final JPanel welcomePanel; - - private final TimelineViewPanel timelineViewPanel; - - private final MainFrameStatusPanel statusPanel; - - private final MainFrameMenu mainMenu; - - private final JProgressBar progressBar = new JProgressBar(0, 100); - - public TagTree tagTree; - - public DumpTree dumpTree; - - private final FlashPlayerPanel flashPanel; - - private final JPanel contentPanel; - - private final JPanel displayPanel; - - public FolderPreviewPanel folderPreviewPanel; - - private boolean isWelcomeScreen = true; - - private static final String CARDPREVIEWPANEL = "Preview card"; - - private static final String CARDFOLDERPREVIEWPANEL = "Folder preview card"; - - private static final String CARDEMPTYPANEL = "Empty card"; - - private static final String CARDDUMPVIEW = "Dump view"; - - private static final String CARDACTIONSCRIPTPANEL = "ActionScript card"; - - private static final String CARDACTIONSCRIPT3PANEL = "ActionScript3 card"; - - private static final String CARDHEADER = "Header card"; - - private static final String DETAILCARDAS3NAVIGATOR = "Traits list"; - - private static final String DETAILCARDTAGINFO = "Tag information"; - - private static final String DETAILCARDEMPTYPANEL = "Empty card"; - - private static final String SPLIT_PANE1 = "SPLITPANE1"; - - private static final String WELCOME_PANEL = "WELCOMEPANEL"; - - private static final String TIMELINE_PANEL = "TIMELINEPANEL"; - - private final JPersistentSplitPane splitPane1; - - private final JPersistentSplitPane splitPane2; - - private JPanel detailPanel; - - private JTextField filterField = new MyTextField(""); - - private JPanel searchPanel; - - private ABCPanel abcPanel; - - private ActionPanel actionPanel; - - private final PreviewPanel previewPanel; - - private final HeaderInfoPanel headerPanel; - - private DumpViewPanel dumpViewPanel; - - private final JPanel treePanel; - - private final TagInfoPanel tagInfoPanel; - - private TreePanelMode treePanelMode; - - private CancellableWorker setSourceWorker; - - public TreeItem oldItem; - - // play morph shape in 2 second(s) - public static final int MORPH_SHAPE_ANIMATION_LENGTH = 2; - - public static final int MORPH_SHAPE_ANIMATION_FRAME_RATE = 30; - - private static final Logger logger = Logger.getLogger(MainPanel.class.getName()); - - public void setPercent(int percent) { - progressBar.setValue(percent); - progressBar.setVisible(true); - } - - public void hidePercent() { - if (progressBar.isVisible()) { - progressBar.setVisible(false); - } - } - - public MainFrame getMainFrame() { - return mainFrame; - } - - static { - try { - File.createTempFile("temp", ".swf").delete(); //First call to this is slow, so make it first - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - private static void addTab(JTabbedPane tabbedPane, Component tab, String title, Icon icon) { - tabbedPane.add(tab); - - JLabel lbl = new JLabel(title); - lbl.setIcon(icon); - lbl.setIconTextGap(5); - lbl.setHorizontalTextPosition(SwingConstants.RIGHT); - - tabbedPane.setTabComponentAt(tabbedPane.getTabCount() - 1, lbl); - } - - public void setStatus(String s) { - statusPanel.setStatus(s); - } - - public void setWorkStatus(String s, CancellableWorker worker) { - statusPanel.setWorkStatus(s, worker); - mainMenu.updateComponents(); - } - - private JPanel createWelcomePanel() { - JPanel welcomePanel = new JPanel(); - welcomePanel.setLayout(new BoxLayout(welcomePanel, BoxLayout.Y_AXIS)); - JLabel welcomeToLabel = new JLabel(translate("startup.welcometo")); - welcomeToLabel.setFont(welcomeToLabel.getFont().deriveFont(40)); - welcomeToLabel.setAlignmentX(0.5f); - JPanel appNamePanel = new JPanel(new FlowLayout()); - JLabel jpLabel = new JLabel("JPEXS "); - jpLabel.setAlignmentX(0.5f); - jpLabel.setForeground(new Color(0, 0, 160)); - jpLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); - jpLabel.setHorizontalAlignment(SwingConstants.CENTER); - appNamePanel.add(jpLabel); - - JLabel ffLabel = new JLabel("Free Flash "); - ffLabel.setAlignmentX(0.5f); - ffLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); - ffLabel.setHorizontalAlignment(SwingConstants.CENTER); - appNamePanel.add(ffLabel); - - JLabel decLabel = new JLabel("Decompiler"); - decLabel.setAlignmentX(0.5f); - decLabel.setForeground(Color.red); - decLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); - decLabel.setHorizontalAlignment(SwingConstants.CENTER); - appNamePanel.add(decLabel); - appNamePanel.setAlignmentX(0.5f); - welcomePanel.add(Box.createGlue()); - welcomePanel.add(welcomeToLabel); - welcomePanel.add(appNamePanel); - JLabel startLabel = new JLabel(translate("startup.selectopen")); - startLabel.setAlignmentX(0.5f); - startLabel.setFont(startLabel.getFont().deriveFont(30)); - welcomePanel.add(startLabel); - welcomePanel.add(Box.createGlue()); - return welcomePanel; - } - - private JPanel createFolderPreviewCard() { - JPanel folderPreviewCard = new JPanel(new BorderLayout()); - folderPreviewPanel = new FolderPreviewPanel(this, new ArrayList<>()); - folderPreviewCard.add(new JScrollPane(folderPreviewPanel), BorderLayout.CENTER); - - return folderPreviewCard; - } - - private JPanel createDumpPreviewCard() { - JPanel dumpViewCard = new JPanel(new BorderLayout()); - dumpViewPanel = new DumpViewPanel(dumpTree); - dumpViewCard.add(new JScrollPane(dumpViewPanel), BorderLayout.CENTER); - - return dumpViewCard; - } - - public String translate(String key) { - return mainFrame.translate(key); - } - - public MainPanel(MainFrame mainFrame, MainFrameMenu mainMenu, FlashPlayerPanel flashPanel) { - super(); - - this.mainFrame = mainFrame; - this.mainMenu = mainMenu; - this.flashPanel = flashPanel; - - mainFrame.setTitle(ApplicationInfo.applicationVerName); - - setLayout(new BorderLayout()); - swfs = new ObservableList<>(); - - detailPanel = new JPanel(); - detailPanel.setLayout(new CardLayout()); - - JPanel whitePanel = new JPanel(); - whitePanel.setBackground(Color.white); - detailPanel.add(whitePanel, DETAILCARDEMPTYPANEL); - - tagInfoPanel = new TagInfoPanel(this); - detailPanel.add(tagInfoPanel, DETAILCARDTAGINFO); - - UIManager.getDefaults().put("TreeUI", BasicTreeUI.class.getName()); - tagTree = new TagTree(null, this); - tagTree.addTreeSelectionListener(this); - tagTree.setSelectionModel(new DefaultTreeSelectionModel() { - - private boolean isModified() { - if (abcPanel != null && abcPanel.isEditing()) { - abcPanel.tryAutoSave(); - } - - if (actionPanel != null && actionPanel.isEditing()) { - actionPanel.tryAutoSave(); - } - - if (previewPanel.isEditing()) { - previewPanel.tryAutoSave(); - } - - if (headerPanel.isEditing()) { - headerPanel.tryAutoSave(); - } - - return (abcPanel != null && abcPanel.isEditing()) - || (actionPanel != null && actionPanel.isEditing()) - || previewPanel.isEditing() || headerPanel.isEditing(); - } - - @Override - public void addSelectionPath(TreePath path) { - if (isModified()) { - return; - } - - super.addSelectionPath(path); - } - - @Override - public void addSelectionPaths(TreePath[] paths) { - if (isModified()) { - return; - } - - super.addSelectionPaths(paths); - } - - @Override - public void setSelectionPath(TreePath path) { - if (isModified()) { - return; - } - - super.setSelectionPath(path); - } - - @Override - public void setSelectionPaths(TreePath[] pPaths) { - if (isModified()) { - return; - } - - super.setSelectionPaths(pPaths); - } - - @Override - public void clearSelection() { - if (isModified()) { - return; - } - - super.clearSelection(); - } - - public void setSelection(TreePath[] selection) { - if (isModified()) { - return; - } - - this.selection = selection; - } - - @Override - public void removeSelectionPath(TreePath path) { - if (isModified()) { - return; - } - - super.removeSelectionPath(path); - } - - @Override - public void removeSelectionPaths(TreePath[] paths) { - if (isModified()) { - return; - } - - super.removeSelectionPaths(paths); - } - }); - - DragSource dragSource = DragSource.getDefaultDragSource(); - dragSource.createDefaultDragGestureRecognizer(tagTree, DnDConstants.ACTION_COPY_OR_MOVE, new DragGestureListener() { - @Override - public void dragGestureRecognized(DragGestureEvent dge) { - dge.startDrag(DragSource.DefaultCopyDrop, new Transferable() { - @Override - public DataFlavor[] getTransferDataFlavors() { - return new DataFlavor[]{DataFlavor.javaFileListFlavor}; - } - - @Override - public boolean isDataFlavorSupported(DataFlavor flavor) { - return flavor.equals(DataFlavor.javaFileListFlavor); - } - - @Override - public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { - if (flavor.equals(DataFlavor.javaFileListFlavor)) { - List files; - String tempDir = System.getProperty("java.io.tmpdir"); - if (!tempDir.endsWith(File.separator)) { - tempDir += File.separator; - } - Random rnd = new Random(); - tempDir += "ffdec" + File.separator + "export" + File.separator + System.currentTimeMillis() + "_" + rnd.nextInt(1000); - File fTempDir = new File(tempDir); - Path.createDirectorySafe(fTempDir); - - File ftemp = new File(tempDir); - ExportDialog exd = new ExportDialog(null); - try { - files = exportSelection(new GuiAbortRetryIgnoreHandler(), tempDir, exd); - } catch (InterruptedException ex) { - Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex); - return null; - } - - files.clear(); - - File[] fs = ftemp.listFiles(); - files.addAll(Arrays.asList(fs)); - - Main.stopWork(); - - for (File f : files) { - f.deleteOnExit(); - } - new File(tempDir).deleteOnExit(); - return files; - - } - return null; - } - }, new DragSourceListener() { - @Override - public void dragEnter(DragSourceDragEvent dsde) { - enableDrop(false); - } - - @Override - public void dragOver(DragSourceDragEvent dsde) { - } - - @Override - public void dropActionChanged(DragSourceDragEvent dsde) { - } - - @Override - public void dragExit(DragSourceEvent dse) { - } - - @Override - public void dragDropEnd(DragSourceDropEvent dsde) { - enableDrop(true); - } - }); - } - }); - - tagTree.createContextMenu(); - - dumpTree = new DumpTree(null, this); - dumpTree.addTreeSelectionListener(this); - dumpTree.createContextMenu(); - - statusPanel = new MainFrameStatusPanel(this); - add(statusPanel, BorderLayout.SOUTH); - - displayPanel = new JPanel(new CardLayout()); - - DefaultSyntaxKit.initKit(); - previewPanel = new PreviewPanel(this, flashPanel); - displayPanel.add(previewPanel, CARDPREVIEWPANEL); - displayPanel.add(createFolderPreviewCard(), CARDFOLDERPREVIEWPANEL); - displayPanel.add(createDumpPreviewCard(), CARDDUMPVIEW); - - headerPanel = new HeaderInfoPanel(); - displayPanel.add(headerPanel, CARDHEADER); - - displayPanel.add(new JPanel(), CARDEMPTYPANEL); - showCard(CARDEMPTYPANEL); - - searchPanel = new JPanel(); - searchPanel.setLayout(new BorderLayout()); - searchPanel.add(filterField, BorderLayout.CENTER); - searchPanel.add(new JLabel(View.getIcon("search16")), BorderLayout.WEST); - JLabel closeSearchButton = new JLabel(View.getIcon("cancel16")); - closeSearchButton.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - filterField.setText(""); - doFilter(); - searchPanel.setVisible(false); - } - }); - searchPanel.add(closeSearchButton, BorderLayout.EAST); - treePanel = new JPanel(new BorderLayout()); - treePanel.add(searchPanel, BorderLayout.SOUTH); - - searchPanel.setVisible(false); - - filterField.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void changedUpdate(DocumentEvent e) { - warn(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - warn(); - } - - @Override - public void insertUpdate(DocumentEvent e) { - warn(); - } - - public void warn() { - doFilter(); - } - }); - - //displayPanel.setBorder(BorderFactory.createLineBorder(Color.black)); - splitPane2 = new JPersistentSplitPane(JSplitPane.VERTICAL_SPLIT, treePanel, detailPanel, Configuration.guiSplitPane2DividerLocationPercent); - splitPane1 = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, splitPane2, displayPanel, Configuration.guiSplitPane1DividerLocationPercent); - - welcomePanel = createWelcomePanel(); - add(welcomePanel, BorderLayout.CENTER); - - timelineViewPanel = new TimelineViewPanel(); - - CardLayout cl3 = new CardLayout(); - contentPanel = new JPanel(cl3); - contentPanel.add(welcomePanel, WELCOME_PANEL); - contentPanel.add(splitPane1, SPLIT_PANE1); - contentPanel.add(timelineViewPanel, TIMELINE_PANEL); - add(contentPanel); - cl3.show(contentPanel, WELCOME_PANEL); - - tagTree.addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(KeyEvent e) { - if ((e.getKeyCode() == 'F') && (e.isControlDown())) { - searchPanel.setVisible(true); - filterField.requestFocusInWindow(); - } - } - }); - detailPanel.setVisible(false); - - updateUi(); - - this.swfs.addCollectionChangedListener((e) -> { - TagTreeModel ttm = tagTree.getModel(); - if (ttm != null) { - if (getCurrentSwf() == null) { - tagTree.setSelectionPath(ttm.getTreePath(ttm.getRoot())); - } - ttm.updateSwfs(e); - tagTree.expandRoot(); - tagTree.expandFirstLevelNodes(); - } - - DumpTreeModel dtm = dumpTree.getModel(); - if (dtm != null) { - List> expandedNodes = View.getExpandedNodes(dumpTree); - dtm.updateSwfs(); - View.expandTreeNodes(dumpTree, expandedNodes); - dumpTree.expandRoot(); - dumpTree.expandFirstLevelNodes(); - } - - if (swfs.isEmpty()) { - tagTree.setUI(new BasicTreeUI() { - { - setHashColor(Color.gray); - } - }); - dumpTree.setUI(new BasicTreeUI() { - { - setHashColor(Color.gray); - } - }); - } - }); - - //Opening files with drag&drop to main window - enableDrop(true); - } - - public void load(SWFList newSwfs, boolean first) { - - previewPanel.clear(); - - for (SWF swf : newSwfs) { - - if (Configuration.autoRenameIdentifiers.get()) { - try { - swf.deobfuscateIdentifiers(RenameType.TYPENUMBER); - swf.assignClassesToSymbols(); - swf.clearScriptCache(); - if (abcPanel != null) { - abcPanel.reload(); - } - - updateClassesList(); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - } - - swfs.add(newSwfs); - SWF swf = newSwfs.size() > 0 ? newSwfs.get(0) : null; - if (swf != null) { - updateUi(swf); - } - - reload(false); - } - - private ABCPanel getABCPanel() { - if (abcPanel == null) { - abcPanel = new ABCPanel(this); - displayPanel.add(abcPanel, CARDACTIONSCRIPT3PANEL); - detailPanel.add(abcPanel.tabbedPane, DETAILCARDAS3NAVIGATOR); - } - - return abcPanel; - } - - private ActionPanel getActionPanel() { - if (actionPanel == null) { - actionPanel = new ActionPanel(MainPanel.this); - displayPanel.add(actionPanel, CARDACTIONSCRIPTPANEL); - } - - return actionPanel; - } - - private void updateUi(final SWF swf) { - - mainFrame.setTitle(ApplicationInfo.applicationVerName + (Configuration.displayFileName.get() ? " - " + swf.getFileTitle() : "")); - - List abcList = swf.getAbcList(); - - boolean hasAbc = !abcList.isEmpty(); - - if (hasAbc) { - getABCPanel().setAbc(abcList.get(0).getABC()); - } - - if (isWelcomeScreen) { - CardLayout cl = (CardLayout) (contentPanel.getLayout()); - cl.show(contentPanel, SPLIT_PANE1); - isWelcomeScreen = false; - } - - mainMenu.updateComponents(swf); - } - - private void updateUi() { - if (!isWelcomeScreen && swfs.isEmpty()) { - CardLayout cl = (CardLayout) (contentPanel.getLayout()); - cl.show(contentPanel, WELCOME_PANEL); - isWelcomeScreen = true; - } - - mainFrame.setTitle(ApplicationInfo.applicationVerName); - mainMenu.updateComponents(null); - - showView(getCurrentView()); - } - - private boolean closeConfirmation(SWFList swfList) { - if (swfList == null) { - if (View.showConfirmDialog(this, translate("message.confirm.closeAll"), translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, Configuration.showCloseConfirmation, JOptionPane.OK_OPTION) != JOptionPane.OK_OPTION) { - return false; - } - } else { - if (View.showConfirmDialog(this, translate("message.confirm.close").replace("{swfName}", swfList.toString()), translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, Configuration.showCloseConfirmation, JOptionPane.OK_OPTION) != JOptionPane.OK_OPTION) { - return false; - } - } - - return true; - } - - public boolean closeAll() { - boolean modified = false; - for (SWFList swfList : swfs) { - for (SWF swf : swfList) { - if (swf.isModified()) { - modified = true; - } - } - } - - if (modified) { - boolean closeConfirmResult = closeConfirmation(swfs.size() == 1 ? swfs.get(0) : null); - if (!closeConfirmResult) { - return false; - } - } - - List swfsLists = new ArrayList<>(swfs); - swfs.clear(); - oldItem = null; - clear(); - updateUi(); - - for (SWFList swfList : swfsLists) { - List swfs2 = new ArrayList<>(swfList); - for (SWF swf : swfs2) { - swf.clearTagSwfs(); - } - } - - return true; - } - - public boolean close(SWFList swfList) { - boolean modified = false; - for (SWF swf : swfList) { - if (swf.isModified()) { - modified = true; - } - } - - if (modified) { - boolean closeConfirmResult = closeConfirmation(swfList); - if (!closeConfirmResult) { - return false; - } - } - - swfs.remove(swfList); - oldItem = null; - clear(); - updateUi(); - - List swfs2 = new ArrayList<>(swfList); - for (SWF swf : swfs2) { - swf.clearTagSwfs(); - } - - return true; - } - - public void enableDrop(boolean value) { - if (value) { - setDropTarget(new DropTarget() { - @Override - public synchronized void drop(DropTargetDropEvent dtde) { - try { - dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); - @SuppressWarnings("unchecked") - List droppedFiles = (List) dtde.getTransferable().getTransferData(DataFlavor.javaFileListFlavor); - if (!droppedFiles.isEmpty()) { - SWFSourceInfo[] sourceInfos = new SWFSourceInfo[droppedFiles.size()]; - for (int i = 0; i < droppedFiles.size(); i++) { - sourceInfos[i] = new SWFSourceInfo(null, droppedFiles.get(i).getAbsolutePath(), null); - } - Main.openFile(sourceInfos, null); - } - } catch (UnsupportedFlavorException | IOException ex) { - } - } - }); - } else { - setDropTarget(null); - } - } - - public void updateClassesList() { - List nodes = getASTreeNodes(tagTree); - boolean updateNeeded = false; - for (TreeItem n : nodes) { - if (n instanceof ClassesListTreeModel) { - ((ClassesListTreeModel) n).update(); - updateNeeded = true; - } - } - - refreshTree(); - - if (updateNeeded) { - tagTree.updateUI(); - } - } - - public void doFilter() { - List nodes = getASTreeNodes(tagTree); - for (TreeItem n : nodes) { - if (n instanceof ClassesListTreeModel) { - ((ClassesListTreeModel) n).setFilter(filterField.getText()); - TagTreeModel tm = tagTree.getModel(); - TreePath path = tm.getTreePath(n); - tm.updateNode(path); - View.expandTreeNodes(tagTree, path, true); - } - } - } - - public void renameIdentifier(SWF swf, String identifier) throws InterruptedException { - String oldName = identifier; - String newName = View.showInputDialog(translate("rename.enternew"), oldName); - if (newName != null) { - if (!oldName.equals(newName)) { - swf.renameAS2Identifier(oldName, newName); - View.showMessageDialog(null, translate("rename.finished.identifier")); - updateClassesList(); - reload(true); - } - } - } - - public void renameMultiname(List abcList, int multiNameIndex) { - String oldName = ""; - AVM2ConstantPool constants = getABCPanel().abc.constants; - if (constants.getMultiname(multiNameIndex).name_index > 0) { - oldName = constants.getString(constants.getMultiname(multiNameIndex).name_index); - } - - String newName = View.showInputDialog(translate("rename.enternew"), oldName); - if (newName != null) { - if (!oldName.equals(newName)) { - int mulCount = 0; - for (ABCContainerTag cnt : abcList) { - ABC abc = cnt.getABC(); - for (int m = 1; m < abc.constants.getMultinameCount(); m++) { - int ni = abc.constants.getMultiname(m).name_index; - String n = ""; - if (ni > 0) { - n = abc.constants.getString(ni); - } - if (n.equals(oldName)) { - abc.renameMultiname(m, newName); - mulCount++; - } - } - } - - View.showMessageDialog(null, translate("rename.finished.multiname").replace("%count%", Integer.toString(mulCount))); - if (abcPanel != null) { - abcPanel.reload(); - } - - updateClassesList(); - reload(true); - ABCPanel abcPanel = getABCPanel(); - abcPanel.hilightScript(abcPanel.getSwf(), abcPanel.decompiledTextArea.getScriptLeaf().getClassPath().toString()); - } - } - } - - public List getASTreeNodes(TagTree tree) { - List result = new ArrayList<>(); - TagTreeModel tm = (TagTreeModel) tree.getModel(); - if (tm == null) { - return result; - } - TreeItem root = tm.getRoot(); - for (int i = 0; i < tm.getChildCount(root); i++) { - // first level node can be SWF and SWFBundle - TreeItem node = tm.getChild(root, i); - if (node instanceof SWFBundle) { - for (int j = 0; j < tm.getChildCount(node); j++) { - // child of SWFBundle should be SWF - SWF swfNode = (SWF) tm.getChild(node, j); - result.add(tm.getScriptsNode(swfNode)); - } - } else if (node instanceof SWF) { - SWF swfNode = (SWF) tm.getChild(root, i); - result.add(tm.getScriptsNode(swfNode)); - } - } - return result; - } - - public boolean confirmExperimental() { - return View.showConfirmDialog(null, translate("message.confirm.experimental"), translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION; - } - - public List exportSelection(AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException, InterruptedException { - - List ret = new ArrayList<>(); - List sel = folderPreviewPanel.selectedItems.isEmpty() ? tagTree.getAllSelected(tagTree) : new ArrayList<>(folderPreviewPanel.selectedItems.values()); - - List allSwfs = new ArrayList<>(); - for (SWFList swfList : swfs) { - for (SWF swf : swfList) { - allSwfs.add(swf); - } - } - - for (int j = 0; j < allSwfs.size(); j++) { - List as3scripts = new ArrayList<>(); - List images = new ArrayList<>(); - List shapes = new ArrayList<>(); - List morphshapes = new ArrayList<>(); - List buttons = new ArrayList<>(); - List movies = new ArrayList<>(); - List sounds = new ArrayList<>(); - List texts = new ArrayList<>(); - List as12scripts = new ArrayList<>(); - List binaryData = new ArrayList<>(); - Map> frames = new HashMap<>(); - List fonts = new ArrayList<>(); - List symbolNames = new ArrayList<>(); - - SWF swf = allSwfs.get(j); - for (TreeItem d : sel) { - SWF selectedNodeSwf = d.getSwf(); - if (!allSwfs.contains(selectedNodeSwf)) { - allSwfs.add(selectedNodeSwf); - } - - if (selectedNodeSwf != swf) { - continue; - } - - if (d instanceof TagScript) { - Tag tag = ((TagScript) d).getTag(); - if (tag instanceof DoActionTag || tag instanceof DoInitActionTag) { - as12scripts.add(d); - } - } - - if (d instanceof Tag || d instanceof ASMSource) { - TreeNodeType nodeType = TagTree.getTreeNodeType(d); - if (nodeType == TreeNodeType.IMAGE) { - images.add((Tag) d); - } - if (nodeType == TreeNodeType.SHAPE) { - shapes.add((Tag) d); - } - if (nodeType == TreeNodeType.BUTTON) { - buttons.add((Tag) d); - } - if (nodeType == TreeNodeType.MORPH_SHAPE) { - morphshapes.add((Tag) d); - } - if (nodeType == TreeNodeType.AS) { - as12scripts.add(d); - } - if (nodeType == TreeNodeType.MOVIE) { - movies.add((Tag) d); - } - if (nodeType == TreeNodeType.SOUND) { - sounds.add((Tag) d); - } - if (nodeType == TreeNodeType.BINARY_DATA) { - binaryData.add((Tag) d); - } - if (nodeType == TreeNodeType.TEXT) { - texts.add((Tag) d); - } - if (nodeType == TreeNodeType.FONT) { - fonts.add((Tag) d); - } - if (nodeType == TreeNodeType.OTHER_TAG) { - if (d instanceof SymbolClassTypeTag) { - symbolNames.add((Tag) d); - } - } - } - - if (d instanceof Frame) { - Frame fn = (Frame) d; - Timelined parent = fn.timeline.timelined; - int frame = fn.frame; - int parentId = 0; - if (parent instanceof CharacterTag) { - parentId = ((CharacterTag) parent).getCharacterId(); - } - if (!frames.containsKey(parentId)) { - frames.put(parentId, new ArrayList<>()); - } - frames.get(parentId).add(frame); - } - if (d instanceof ScriptPack) { - as3scripts.add((ScriptPack) d); - } - } - - EventListener evl = swf.getExportEventListener(); - - if (export.isOptionEnabled(ImageExportMode.class)) { - ret.addAll(new ImageExporter().exportImages(handler, selFile + File.separator + ImageExportSettings.EXPORT_FOLDER_NAME, images, - new ImageExportSettings(export.getValue(ImageExportMode.class)), evl)); - } - - if (export.isOptionEnabled(ShapeExportMode.class)) { - ret.addAll(new ShapeExporter().exportShapes(handler, selFile + File.separator + ShapeExportSettings.EXPORT_FOLDER_NAME, shapes, - new ShapeExportSettings(export.getValue(ShapeExportMode.class), export.getZoom()), evl)); - } - - if (export.isOptionEnabled(MorphShapeExportMode.class)) { - ret.addAll(new MorphShapeExporter().exportMorphShapes(handler, selFile + File.separator + MorphShapeExportSettings.EXPORT_FOLDER_NAME, morphshapes, - new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class), export.getZoom()), evl)); - } - - if (export.isOptionEnabled(TextExportMode.class)) { - ret.addAll(new TextExporter().exportTexts(handler, selFile + File.separator + TextExportSettings.EXPORT_FOLDER_NAME, texts, - new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get(), export.getZoom()), evl)); - } - - if (export.isOptionEnabled(MovieExportMode.class)) { - ret.addAll(new MovieExporter().exportMovies(handler, selFile + File.separator + MovieExportSettings.EXPORT_FOLDER_NAME, movies, - new MovieExportSettings(export.getValue(MovieExportMode.class)), evl)); - } - - if (export.isOptionEnabled(SoundExportMode.class)) { - ret.addAll(new SoundExporter().exportSounds(handler, selFile + File.separator + SoundExportSettings.EXPORT_FOLDER_NAME, sounds, - new SoundExportSettings(export.getValue(SoundExportMode.class)), evl)); - } - - if (export.isOptionEnabled(BinaryDataExportMode.class)) { - ret.addAll(new BinaryDataExporter().exportBinaryData(handler, selFile + File.separator + BinaryDataExportSettings.EXPORT_FOLDER_NAME, binaryData, - new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class)), evl)); - } - - if (export.isOptionEnabled(FontExportMode.class)) { - ret.addAll(new FontExporter().exportFonts(handler, selFile + File.separator + FontExportSettings.EXPORT_FOLDER_NAME, fonts, - new FontExportSettings(export.getValue(FontExportMode.class)), evl)); - } - - if (export.isOptionEnabled(SymbolClassExportMode.class)) { - ret.addAll(new SymbolClassExporter().exportNames(selFile, symbolNames, evl)); - } - - FrameExporter frameExporter = new FrameExporter(); - - if (export.isOptionEnabled(FrameExportMode.class)) { - FrameExportSettings fes = new FrameExportSettings(export.getValue(FrameExportMode.class), export.getZoom()); - for (Entry> entry : frames.entrySet()) { - int containerId = entry.getKey(); - if (containerId == 0) { - String subFolder = FrameExportSettings.EXPORT_FOLDER_NAME; - ret.addAll(frameExporter.exportFrames(handler, selFile + File.separator + subFolder, swf, containerId, entry.getValue(), fes, evl)); - } - } - } - - if (export.isOptionEnabled(SpriteExportMode.class)) { - SpriteExportSettings ses = new SpriteExportSettings(export.getValue(SpriteExportMode.class), export.getZoom()); - for (Entry> entry : frames.entrySet()) { - int containerId = entry.getKey(); - if (containerId != 0) { - String subFolder = SpriteExportSettings.EXPORT_FOLDER_NAME; - ret.addAll(frameExporter.exportFrames(handler, selFile + File.separator + subFolder, swf, containerId, entry.getValue(), ses, evl)); - } - } - } - - if (export.isOptionEnabled(ButtonExportMode.class)) { - ButtonExportSettings bes = new ButtonExportSettings(export.getValue(ButtonExportMode.class), export.getZoom()); - for (Tag tag : buttons) { - ButtonTag button = (ButtonTag) tag; - String subFolder = ButtonExportSettings.EXPORT_FOLDER_NAME; - List frameNums = new ArrayList<>(); - frameNums.add(0); // todo: export all frames - ret.addAll(frameExporter.exportFrames(handler, selFile + File.separator + subFolder, swf, button.getCharacterId(), frameNums, bes, evl)); - } - } - - if (export.isOptionEnabled(ScriptExportMode.class)) { - if (as3scripts.size() > 0 || as12scripts.size() > 0) { - boolean parallel = Configuration.parallelSpeedUp.get(); - String scriptsFolder = Path.combine(selFile, ScriptExportSettings.EXPORT_FOLDER_NAME); - Path.createDirectorySafe(new File(scriptsFolder)); - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), !parallel && Configuration.scriptExportSingleFile.get()); - String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); - try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { - scriptExportSettings.singleFileWriter = writer; - if (swf.isAS3()) { - ret.addAll(new AS3ScriptExporter().exportActionScript3(swf, handler, scriptsFolder, as3scripts, scriptExportSettings, parallel, evl)); - } else { - Map asmsToExport = swf.getASMs(true, as12scripts, false); - ret.addAll(new AS2ScriptExporter().exportAS2Scripts(handler, scriptsFolder, asmsToExport, scriptExportSettings, parallel, evl)); - } - } - } - } - } - - return ret; - } - - public void exportAll(SWF swf, AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException, InterruptedException { - boolean exportAll = false; - if (exportAll) { - exportAllDebug(swf, handler, selFile, export); - return; - } - - EventListener evl = swf.getExportEventListener(); - - if (export.isOptionEnabled(ImageExportMode.class)) { - new ImageExporter().exportImages(handler, Path.combine(selFile, ImageExportSettings.EXPORT_FOLDER_NAME), swf.tags, - new ImageExportSettings(export.getValue(ImageExportMode.class)), evl); - } - - if (export.isOptionEnabled(ShapeExportMode.class)) { - new ShapeExporter().exportShapes(handler, Path.combine(selFile, ShapeExportSettings.EXPORT_FOLDER_NAME), swf.tags, - new ShapeExportSettings(export.getValue(ShapeExportMode.class), export.getZoom()), evl); - } - - if (export.isOptionEnabled(MorphShapeExportMode.class)) { - new MorphShapeExporter().exportMorphShapes(handler, Path.combine(selFile, MorphShapeExportSettings.EXPORT_FOLDER_NAME), swf.tags, - new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class), export.getZoom()), evl); - } - - if (export.isOptionEnabled(TextExportMode.class)) { - new TextExporter().exportTexts(handler, Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME), swf.tags, - new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get(), export.getZoom()), evl); - } - - if (export.isOptionEnabled(MovieExportMode.class)) { - new MovieExporter().exportMovies(handler, Path.combine(selFile, MovieExportSettings.EXPORT_FOLDER_NAME), swf.tags, - new MovieExportSettings(export.getValue(MovieExportMode.class)), evl); - } - - if (export.isOptionEnabled(SoundExportMode.class)) { - new SoundExporter().exportSounds(handler, Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME), swf.tags, - new SoundExportSettings(export.getValue(SoundExportMode.class)), evl); - } - - if (export.isOptionEnabled(BinaryDataExportMode.class)) { - new BinaryDataExporter().exportBinaryData(handler, Path.combine(selFile, BinaryDataExportSettings.EXPORT_FOLDER_NAME), swf.tags, - new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class)), evl); - } - - if (export.isOptionEnabled(FontExportMode.class)) { - new FontExporter().exportFonts(handler, Path.combine(selFile, FontExportSettings.EXPORT_FOLDER_NAME), swf.tags, - new FontExportSettings(export.getValue(FontExportMode.class)), evl); - } - - if (export.isOptionEnabled(SymbolClassExportMode.class)) { - new SymbolClassExporter().exportNames(selFile, swf.tags, evl); - } - - FrameExporter frameExporter = new FrameExporter(); - - if (export.isOptionEnabled(FrameExportMode.class)) { - FrameExportSettings fes = new FrameExportSettings(export.getValue(FrameExportMode.class), export.getZoom()); - frameExporter.exportFrames(handler, Path.combine(selFile, FrameExportSettings.EXPORT_FOLDER_NAME), swf, 0, null, fes, evl); - } - - if (export.isOptionEnabled(SpriteExportMode.class)) { - SpriteExportSettings ses = new SpriteExportSettings(export.getValue(SpriteExportMode.class), export.getZoom()); - for (CharacterTag c : swf.getCharacters().values()) { - if (c instanceof DefineSpriteTag) { - frameExporter.exportFrames(handler, Path.combine(selFile, SpriteExportSettings.EXPORT_FOLDER_NAME), swf, c.getCharacterId(), null, ses, evl); - } - } - } - - if (export.isOptionEnabled(ButtonExportMode.class)) { - ButtonExportSettings bes = new ButtonExportSettings(export.getValue(ButtonExportMode.class), export.getZoom()); - for (CharacterTag c : swf.getCharacters().values()) { - if (c instanceof ButtonTag) { - List frameNums = new ArrayList<>(); - frameNums.add(0); // todo: export all frames - frameExporter.exportFrames(handler, Path.combine(selFile, ButtonExportSettings.EXPORT_FOLDER_NAME), swf, c.getCharacterId(), frameNums, bes, evl); - } - } - } - - if (export.isOptionEnabled(ScriptExportMode.class)) { - boolean parallel = Configuration.parallelSpeedUp.get(); - String scriptsFolder = Path.combine(selFile, ScriptExportSettings.EXPORT_FOLDER_NAME); - Path.createDirectorySafe(new File(scriptsFolder)); - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), !parallel && Configuration.scriptExportSingleFile.get()); - String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); - try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { - scriptExportSettings.singleFileWriter = writer; - swf.exportActionScript(handler, scriptsFolder, scriptExportSettings, parallel, evl); - } - } - } - - public void exportAllDebug(SWF swf, AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException, InterruptedException { - EventListener evl = swf.getExportEventListener(); - - if (export.isOptionEnabled(ImageExportMode.class)) { - for (ImageExportMode exportMode : ImageExportMode.values()) { - new ImageExporter().exportImages(handler, Path.combine(selFile, ImageExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, - new ImageExportSettings(exportMode), evl); - } - } - - if (export.isOptionEnabled(ShapeExportMode.class)) { - for (ShapeExportMode exportMode : ShapeExportMode.values()) { - new ShapeExporter().exportShapes(handler, Path.combine(selFile, ShapeExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, - new ShapeExportSettings(exportMode, export.getZoom()), evl); - } - } - - if (export.isOptionEnabled(MorphShapeExportMode.class)) { - for (MorphShapeExportMode exportMode : MorphShapeExportMode.values()) { - new MorphShapeExporter().exportMorphShapes(handler, Path.combine(selFile, MorphShapeExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, - new MorphShapeExportSettings(exportMode, export.getZoom()), evl); - } - } - - if (export.isOptionEnabled(TextExportMode.class)) { - for (TextExportMode exportMode : TextExportMode.values()) { - new TextExporter().exportTexts(handler, Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, - new TextExportSettings(exportMode, Configuration.textExportSingleFile.get(), export.getZoom()), evl); - } - } - - if (export.isOptionEnabled(MovieExportMode.class)) { - for (MovieExportMode exportMode : MovieExportMode.values()) { - new MovieExporter().exportMovies(handler, Path.combine(selFile, MovieExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, - new MovieExportSettings(exportMode), evl); - } - } - - if (export.isOptionEnabled(SoundExportMode.class)) { - for (SoundExportMode exportMode : SoundExportMode.values()) { - new SoundExporter().exportSounds(handler, Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, - new SoundExportSettings(exportMode), evl); - } - } - - if (export.isOptionEnabled(BinaryDataExportMode.class)) { - for (BinaryDataExportMode exportMode : BinaryDataExportMode.values()) { - new BinaryDataExporter().exportBinaryData(handler, Path.combine(selFile, BinaryDataExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, - new BinaryDataExportSettings(exportMode), evl); - } - } - - if (export.isOptionEnabled(FontExportMode.class)) { - for (FontExportMode exportMode : FontExportMode.values()) { - new FontExporter().exportFonts(handler, Path.combine(selFile, FontExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, - new FontExportSettings(exportMode), evl); - } - } - - if (export.isOptionEnabled(SymbolClassExportMode.class)) { - for (SymbolClassExportMode exportMode : SymbolClassExportMode.values()) { - new SymbolClassExporter().exportNames(selFile, swf.tags, evl); - } - } - - FrameExporter frameExporter = new FrameExporter(); - - if (export.isOptionEnabled(FrameExportMode.class)) { - for (FrameExportMode exportMode : FrameExportMode.values()) { - FrameExportSettings fes = new FrameExportSettings(exportMode, export.getZoom()); - frameExporter.exportFrames(handler, Path.combine(selFile, FrameExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, 0, null, fes, evl); - } - } - - if (export.isOptionEnabled(SpriteExportMode.class)) { - for (SpriteExportMode exportMode : SpriteExportMode.values()) { - SpriteExportSettings ses = new SpriteExportSettings(exportMode, export.getZoom()); - for (CharacterTag c : swf.getCharacters().values()) { - if (c instanceof DefineSpriteTag) { - frameExporter.exportFrames(handler, Path.combine(selFile, SpriteExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, c.getCharacterId(), null, ses, evl); - } - } - } - } - - if (export.isOptionEnabled(ButtonExportMode.class)) { - for (ButtonExportMode exportMode : ButtonExportMode.values()) { - ButtonExportSettings bes = new ButtonExportSettings(exportMode, export.getZoom()); - for (CharacterTag c : swf.getCharacters().values()) { - if (c instanceof ButtonTag) { - List frameNums = new ArrayList<>(); - frameNums.add(0); // todo: export all frames - frameExporter.exportFrames(handler, Path.combine(selFile, ButtonExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, c.getCharacterId(), frameNums, bes, evl); - } - } - } - } - - if (export.isOptionEnabled(ScriptExportMode.class)) { - boolean parallel = Configuration.parallelSpeedUp.get(); - for (ScriptExportMode exportMode : ScriptExportMode.values()) { - String scriptsFolder = Path.combine(selFile, ScriptExportSettings.EXPORT_FOLDER_NAME, exportMode.name()); - Path.createDirectorySafe(new File(scriptsFolder)); - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(exportMode, !parallel && Configuration.scriptExportSingleFile.get()); - String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); - try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { - scriptExportSettings.singleFileWriter = writer; - swf.exportActionScript(handler, scriptsFolder, scriptExportSettings, parallel, evl); - } - } - } - } - - public List getSwfs() { - return swfs; - } - - public SWFList getCurrentSwfList() { - SWF swf = getCurrentSwf(); - if (swf == null) { - return null; - } - - return swf.swfList; - } - - public SWF getCurrentSwf() { - if (swfs == null || swfs.isEmpty()) { - return null; - } - - if (treePanelMode == TreePanelMode.TAG_TREE) { - TreeItem treeNode = (TreeItem) tagTree.getLastSelectedPathComponent(); - if (treeNode == null) { - return swfs.get(0).get(0); - } - - if (treeNode instanceof SWFList) { - return null; - } - - return treeNode.getSwf(); - } else if (treePanelMode == TreePanelMode.DUMP_TREE) { - DumpInfo dumpInfo = (DumpInfo) dumpTree.getLastSelectedPathComponent(); - - if (dumpInfo == null) { - return null; - } - - return DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); - } - - return null; - } - - public void gotoFrame(int frame) { - TreeItem treeItem = (TreeItem) tagTree.getLastSelectedPathComponent(); - if (treeItem == null) { - return; - } - if (treeItem instanceof Timelined) { - Timelined t = (Timelined) treeItem; - Frame f = tagTree.getModel().getFrame(treeItem.getSwf(), t, frame); - if (f != null) { - setTagTreeSelectedNode(f); - } - } - } - - public void gotoDocumentClass(SWF swf) { - if (swf == null) { - return; - } - - String documentClass = swf.getDocumentClass(); - if (documentClass != null && !Configuration.dumpView.get()) { - List abcList = swf.getAbcList(); - if (!abcList.isEmpty()) { - ABCPanel abcPanel = getABCPanel(); - abcPanel.setAbc(abcList.get(0).getABC()); - abcPanel.hilightScript(swf, documentClass); - } - } - } - - public void disableDecompilationChanged() { - clearAllScriptCache(); - - if (abcPanel != null) { - abcPanel.reload(); - } - - updateClassesList(); - } - - private void clearAllScriptCache() { - for (SWFList swfList : swfs) { - for (SWF swf : swfList) { - swf.clearScriptCache(); - } - } - } - - public void searchInActionScriptOrText(Boolean searchInText) { - SearchDialog searchDialog = new SearchDialog(getMainFrame().getWindow(), false); - if (searchInText != null) { - if (searchInText) { - searchDialog.searchInTextsRadioButton.setSelected(true); - } else { - searchDialog.searchInASRadioButton.setSelected(true); - } - } - - if (searchDialog.showDialog() == AppDialog.OK_OPTION) { - final String txt = searchDialog.searchField.getText(); - if (!txt.isEmpty()) { - final SWF swf = getCurrentSwf(); - if (swf.isAS3()) { - getABCPanel(); - } else { - getActionPanel(); - } - - boolean ignoreCase = searchDialog.ignoreCaseCheckBox.isSelected(); - boolean regexp = searchDialog.regexpCheckBox.isSelected(); - - if (searchDialog.searchInASRadioButton.isSelected()) { - new CancellableWorker() { - @Override - protected Void doInBackground() throws Exception { - List abcResult = null; - List actionResult = null; - if (swf.isAS3()) { - abcResult = getABCPanel().search(txt, ignoreCase, regexp, this); - } else { - actionResult = getActionPanel().search(txt, ignoreCase, regexp, this); - } - - List fAbcResult = abcResult; - List fActionResult = actionResult; - View.execInEventDispatch(() -> { - boolean found = false; - if (fAbcResult != null) { - found = true; - getABCPanel().searchPanel.setSearchText(txt); - SearchResultsDialog sr = new SearchResultsDialog<>(getMainFrame().getWindow(), txt, getABCPanel()); - sr.setResults(fAbcResult); - sr.setVisible(true); - } else if (fActionResult != null) { - found = true; - getActionPanel().searchPanel.setSearchText(txt); - - SearchResultsDialog sr = new SearchResultsDialog<>(getMainFrame().getWindow(), txt, getActionPanel()); - sr.setResults(fActionResult); - sr.setVisible(true); - } - - if (!found) { - View.showMessageDialog(null, translate("message.search.notfound").replace("%searchtext%", txt), translate("message.search.notfound.title"), JOptionPane.INFORMATION_MESSAGE); - } - - Main.stopWork(); - }); - - return null; - } - - @Override - protected void done() { - View.execInEventDispatch(() -> { - Main.stopWork(); - }); - - } - }.execute(); - } else if (searchDialog.searchInTextsRadioButton.isSelected()) { - new CancellableWorker() { - @Override - protected Void doInBackground() throws Exception { - List textResult = null; - SearchPanel textSearchPanel = previewPanel.getTextPanel().getSearchPanel(); - textSearchPanel.setOptions(ignoreCase, regexp); - textResult = searchText(txt, ignoreCase, regexp, swf); - - List fTextResult = textResult; - View.execInEventDispatch(() -> { - textSearchPanel.setSearchText(txt); - boolean found = textSearchPanel.setResults(fTextResult); - if (!found) { - View.showMessageDialog(null, translate("message.search.notfound").replace("%searchtext%", txt), translate("message.search.notfound.title"), JOptionPane.INFORMATION_MESSAGE); - } - - Main.stopWork(); - }); - - return null; - } - - @Override - protected void done() { - View.execInEventDispatch(() -> { - Main.stopWork(); - }); - - } - }.execute(); - } - } - } - } - - public void replaceText() { - SearchDialog replaceDialog = new SearchDialog(getMainFrame().getWindow(), true); - if (replaceDialog.showDialog() == AppDialog.OK_OPTION) { - final String txt = replaceDialog.searchField.getText(); - if (!txt.isEmpty()) { - final SWF swf = getCurrentSwf(); - - new CancellableWorker() { - @Override - protected Void doInBackground() throws Exception { - int findCount = 0; - boolean ignoreCase = replaceDialog.ignoreCaseCheckBox.isSelected(); - boolean regexp = replaceDialog.regexpCheckBox.isSelected(); - String replacement = replaceDialog.replaceField.getText(); - if (!regexp) { - replacement = Matcher.quoteReplacement(replacement); - } - Pattern pat; - if (regexp) { - pat = Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); - } else { - pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); - } - List textTags = new ArrayList<>(); - for (Tag tag : swf.tags) { - if (tag instanceof TextTag) { - textTags.add((TextTag) tag); - } - } - for (TextTag textTag : textTags) { - if (!replaceDialog.replaceInParametersCheckBox.isSelected()) { - List texts = textTag.getTexts(); - boolean found = false; - for (int i = 0; i < texts.size(); i++) { - String text = texts.get(i); - if (pat.matcher(text).find()) { - texts.set(i, text.replaceAll(txt, replacement)); - found = true; - findCount++; - } - } - if (found) { - String[] textArray = texts.toArray(new String[texts.size()]); - textTag.setFormattedText(getMissingCharacterHandler(), textTag.getFormattedText().text, textArray); - } - } else { - String text = textTag.getFormattedText().text; - if (pat.matcher(text).find()) { - textTag.setFormattedText(getMissingCharacterHandler(), text.replaceAll(txt, replacement), null); - findCount++; - } - } - } - - if (findCount > 0) { - swf.clearImageCache(); - repaintTree(); - } - - return null; - } - }.execute(); - } - } - } - - private List searchText(String txt, boolean ignoreCase, boolean regexp, SWF swf) { - if (txt != null && !txt.isEmpty()) { - List found = new ArrayList<>(); - Pattern pat; - if (regexp) { - pat = Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); - } else { - pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); - } - for (Tag tag : swf.tags) { - if (tag instanceof TextTag) { - TextTag textTag = (TextTag) tag; - if (pat.matcher(textTag.getFormattedText().text).find()) { - found.add(textTag); - } - } - } - - return found; - } - - return null; - } - - @Override - public void updateSearchPos(TextTag item) { - setTagTreeSelectedNode(item); - previewPanel.getTextPanel().updateSearchPos(); - } - - private void setDumpTreeSelectedNode(DumpInfo dumpInfo) { - DumpTreeModel dtm = (DumpTreeModel) dumpTree.getModel(); - TreePath tp = dtm.getDumpInfoPath(dumpInfo); - if (tp != null) { - dumpTree.setSelectionPath(tp); - dumpTree.scrollPathToVisible(tp); - } else { - showCard(CARDEMPTYPANEL); - } - } - - public void setTagTreeSelectedNode(TreeItem treeItem) { - TagTreeModel ttm = tagTree.getModel(); - TreePath tp = ttm.getTreePath(treeItem); - if (tp != null) { - tagTree.setSelectionPath(tp); - tagTree.scrollPathToVisible(tp); - } else { - showCard(CARDEMPTYPANEL); - } - } - - public void autoDeobfuscateChanged() { - Helper.decompilationErrorAdd = AppStrings.translate(Configuration.autoDeobfuscate.get() ? "deobfuscation.comment.failed" : "deobfuscation.comment.tryenable"); - clearAllScriptCache(); - if (abcPanel != null) { - abcPanel.reload(); - } - reload(true); - updateClassesList(); - } - - public void renameOneIdentifier(final SWF swf) { - if (swf == null) { - return; - } - - FileAttributesTag fileAttributes = swf.getFileAttributes(); - if (fileAttributes != null && fileAttributes.actionScript3) { - final int multiName = getABCPanel().decompiledTextArea.getMultinameUnderCaret(); - final List abcList = swf.getAbcList(); - if (multiName > 0) { - new CancellableWorker() { - @Override - public Void doInBackground() throws Exception { - renameMultiname(abcList, multiName); - return null; - } - - @Override - protected void onStart() { - Main.startWork(translate("work.renaming") + "...", this); - } - - @Override - protected void done() { - Main.stopWork(); - } - }.execute(); - - } else { - View.showMessageDialog(null, translate("message.rename.notfound.multiname"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE); - } - } else { - final String identifier = getActionPanel().getStringUnderCursor(); - if (identifier != null) { - new CancellableWorker() { - @Override - public Void doInBackground() throws Exception { - try { - renameIdentifier(swf, identifier); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - return null; - } - - @Override - protected void onStart() { - Main.startWork(translate("work.renaming") + "...", this); - } - - @Override - protected void done() { - Main.stopWork(); - } - }.execute(); - } else { - View.showMessageDialog(null, translate("message.rename.notfound.identifier"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE); - } - } - } - - public void exportFla(final SWF swf) { - if (swf == null) { - return; - } - JFileChooser fc = new JFileChooser(); - String selDir = Configuration.lastOpenDir.get(); - fc.setCurrentDirectory(new File(selDir)); - if (!selDir.endsWith(File.separator)) { - selDir += File.separator; - } - String fileName = new File(swf.getFile()).getName(); - fileName = fileName.substring(0, fileName.length() - 4) + ".fla"; - fc.setSelectedFile(new File(selDir + fileName)); - List flaFilters = new ArrayList<>(); - List xflFilters = new ArrayList<>(); - List versions = new ArrayList<>(); - boolean isAS3 = swf.isAS3(); - for (int i = FLAVersion.values().length - 1; i >= 0; i--) { - final FLAVersion v = FLAVersion.values()[i]; - if (!isAS3 && v.minASVersion() > 2) { - // This version does not support AS1/2 - } else { - versions.add(v); - FileFilter f = new FileFilter() { - @Override - public boolean accept(File f) { - return f.isDirectory() || (f.getName().toLowerCase().endsWith(".fla")); - } - - @Override - public String getDescription() { - return translate("filter.fla").replace("%version%", v.applicationName()); - } - }; - if (v == FLAVersion.CS6) { - fc.setFileFilter(f); - } else { - fc.addChoosableFileFilter(f); - } - flaFilters.add(f); - f = new FileFilter() { - @Override - public boolean accept(File f) { - return f.isDirectory() || (f.getName().toLowerCase().endsWith(".xfl")); - } - - @Override - public String getDescription() { - return translate("filter.xfl").replace("%version%", v.applicationName()); - } - }; - fc.addChoosableFileFilter(f); - xflFilters.add(f); - } - } - - fc.setAcceptAllFileFilterUsed(false); - JFrame f = new JFrame(); - View.setWindowIcon(f); - if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) { - Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); - File sf = Helper.fixDialogFile(fc.getSelectedFile()); - - FileFilter selectedFilter = fc.getFileFilter(); - final boolean compressed = flaFilters.contains(selectedFilter); - String path = sf.getAbsolutePath(); - if (path.endsWith(".fla") || path.endsWith(".xfl")) { - path = path.substring(0, path.length() - 4); - } - path += compressed ? ".fla" : ".xfl"; - final FLAVersion selectedVersion = versions.get(compressed ? flaFilters.indexOf(selectedFilter) : xflFilters.indexOf(selectedFilter)); - final File selfile = new File(path); - new CancellableWorker() { - @Override - protected Void doInBackground() throws Exception { - Helper.freeMem(); - try { - AbortRetryIgnoreHandler errorHandler = new GuiAbortRetryIgnoreHandler(); - if (compressed) { - swf.exportFla(errorHandler, selfile.getAbsolutePath(), new File(swf.getFile()).getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), selectedVersion); - } else { - swf.exportXfl(errorHandler, selfile.getAbsolutePath(), new File(swf.getFile()).getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), selectedVersion); - } - } catch (Exception ex) { - View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage(), translate("error"), JOptionPane.ERROR_MESSAGE); - } - Helper.freeMem(); - return null; - } - - @Override - protected void onStart() { - Main.startWork(translate("work.exporting.fla") + "...", this); - } - - @Override - protected void done() { - if (Configuration.openFolderAfterFlaExport.get()) { - try { - Desktop.getDesktop().open(selfile.getAbsoluteFile().getParentFile()); - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - Main.stopWork(); - } - }.execute(); - } - } - - public void importText(final SWF swf) { - JFileChooser chooser = new JFileChooser(); - chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); - chooser.setDialogTitle(translate("import.select.directory")); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - chooser.setAcceptAllFileFilterUsed(false); - if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { - String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); - File textsFile = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, TextExporter.TEXT_EXPORT_FILENAME_FORMATTED)); - TextImporter textImporter = new TextImporter(getMissingCharacterHandler(), new TextImportErrorHandler() { - - // "configuration items" for the current replace only - private final ConfigurationItem showAgainImportError = new ConfigurationItem<>("showAgainImportError", true, true); - - private final ConfigurationItem showAgainInvalidText = new ConfigurationItem<>("showAgainInvalidText", true, true); - - private String getTextTagInfo(TextTag textTag) { - StringBuilder ret = new StringBuilder(); - if (textTag != null) { - ret.append(" TextId: ").append(textTag.getCharacterId()).append(" (").append(String.join(", ", textTag.getTexts())).append(")"); - } - - return ret.toString(); - } - - @Override - public boolean handle(TextTag textTag) { - String msg = translate("error.text.import"); - logger.log(Level.SEVERE, msg + getTextTagInfo(textTag)); - return View.showConfirmDialog(MainPanel.this, msg, translate("error"), JOptionPane.OK_CANCEL_OPTION, showAgainImportError, JOptionPane.OK_OPTION) != JOptionPane.OK_OPTION; - } - - @Override - public boolean handle(TextTag textTag, String message, long line) { - String msg = translate("error.text.invalid.continue").replace("%text%", message).replace("%line%", Long.toString(line)); - logger.log(Level.SEVERE, msg + getTextTagInfo(textTag)); - return View.showConfirmDialog(MainPanel.this, msg, translate("error"), JOptionPane.OK_CANCEL_OPTION, showAgainInvalidText, JOptionPane.OK_OPTION) != JOptionPane.OK_OPTION; - } - }); - - // try to import formatted texts - if (textsFile.exists()) { - textImporter.importTextsSingleFileFormatted(textsFile, swf); - } else { - textsFile = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, TextExporter.TEXT_EXPORT_FILENAME_PLAIN)); - // try to import plain texts - if (textsFile.exists()) { - textImporter.importTextsSingleFile(textsFile, swf); - } else { - textImporter.importTextsMultipleFiles(selFile, swf); - } - } - - swf.clearImageCache(); - reload(true); - } - } - - public void importScript(final SWF swf) { - JFileChooser chooser = new JFileChooser(); - chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); - chooser.setDialogTitle(translate("import.select.directory")); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - chooser.setAcceptAllFileFilterUsed(false); - if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { - String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); - String scriptsFolder = Path.combine(selFile, ScriptExportSettings.EXPORT_FOLDER_NAME); - - int countAs2 = new AS2ScriptImporter().importScripts(scriptsFolder, swf.getASMs(true)); - int countAs3 = new AS3ScriptImporter().importScripts(scriptsFolder, swf.getAS3Packs()); - - if (countAs3 > 0) { - updateClassesList(); - } - - if (countAs2 == 0 && countAs3 == 0 && swf.isAS3()) { - View.showMessageDialog(this, translate("import.script.as12warning")); - } else { - View.showMessageDialog(this, translate("import.script.result").replace("%count%", Integer.toString(countAs2))); - reload(true); - } - } - } - - public void importSymbolClass(final SWF swf) { - JFileChooser chooser = new JFileChooser(); - chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); - chooser.setDialogTitle(translate("import.select.directory")); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - chooser.setAcceptAllFileFilterUsed(false); - if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { - String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); - File importFile = new File(Path.combine(selFile, SymbolClassExporter.SYMBOL_CLASS_EXPORT_FILENAME)); - SymbolClassImporter importer = new SymbolClassImporter(); - - if (importFile.exists()) { - importer.importSymbolClasses(importFile, swf); - } - } - } - - private String selectExportDir() { - JFileChooser chooser = new JFileChooser(); - chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); - chooser.setDialogTitle(translate("export.select.directory")); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - chooser.setAcceptAllFileFilterUsed(false); - if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { - final String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); - Configuration.lastExportDir.set(Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath()); - return selFile; - } - return null; - } - - public void export(final boolean onlySel) { - - final SWF swf = getCurrentSwf(); - List sel = tagTree.getSelection(swf); - if (!onlySel) { - sel = null; - } else { - if (sel.isEmpty()) { - return; - } - } - final ExportDialog export = new ExportDialog(sel); - if (export.showExportDialog() == AppDialog.OK_OPTION) { - final String selFile = selectExportDir(); - if (selFile != null) { - final long timeBefore = System.currentTimeMillis(); - - new CancellableWorker() { - @Override - public Void doInBackground() throws Exception { - try { - AbortRetryIgnoreHandler errorHandler = new GuiAbortRetryIgnoreHandler(); - if (onlySel) { - exportSelection(errorHandler, selFile, export); - } else { - exportAll(swf, errorHandler, selFile, export); - } - } catch (Exception ex) { - logger.log(Level.SEVERE, "Error during export", ex); - View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage()); - } - return null; - } - - @Override - protected void onStart() { - Main.startWork(translate("work.exporting") + "...", this); - } - - @Override - protected void done() { - Main.stopWork(); - long timeAfter = System.currentTimeMillis(); - final long timeMs = timeAfter - timeBefore; - - View.execInEventDispatchLater(() -> { - setStatus(translate("export.finishedin").replace("%time%", Helper.formatTimeSec(timeMs))); - }); - } - }.execute(); - - } - } - } - - public void exportJavaSource() { - List sel = tagTree.getSelected(tagTree); - for (TreeItem item : sel) { - if (item instanceof SWF) { - SWF swf = (SWF) item; - final String selFile = selectExportDir(); - if (selFile != null) { - Main.startWork(translate("work.exporting") + "...", null); - - try { - new SwfJavaExporter().exportJavaCode(swf, selFile); - Main.stopWork(); - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - } - } - } - - public void exportSwfXml() { - List sel = tagTree.getSelected(tagTree); - Set swfs = new HashSet<>(); - - for (TreeItem item : sel) { - swfs.add(item.getSwf()); - } - - for (SWF swf : swfs) { - final String selFile = selectExportDir(); - if (selFile != null) { - Main.startWork(translate("work.exporting") + "...", null); - - try { - File outFile = new File(selFile + File.separator + Helper.makeFileName("swf.xml")); - new SwfXmlExporter().exportXml(swf, outFile); - Main.stopWork(); - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - } - } - - public void importSwfXml() { - List sel = tagTree.getSelected(tagTree); - Set swfs = new HashSet<>(); - for (TreeItem item : sel) { - swfs.add(item.getSwf()); - } - if (swfs.size() > 1) { - return; - } - - for (SWF swf : swfs) { - File selectedFile = showImportFileChooser("filter.xml|*.xml"); - if (selectedFile != null) { - File selfile = Helper.fixDialogFile(selectedFile); - String xml = Helper.readTextFile(selfile.getPath()); - try { - new SwfXmlImporter().importSwf(swf, xml); - swf.clearAllCache(); - swf.assignExportNamesToSymbols(); - swf.assignClassesToSymbols(); - refreshTree(swf); - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - } - } - - public void restoreControlFlow(final boolean all) { - if ((!all) || confirmExperimental()) { - new CancellableWorker() { - @Override - protected Void doInBackground() throws Exception { - ABCPanel abcPanel = getABCPanel(); - if (all) { - for (ABCContainerTag tag : abcPanel.getAbcList()) { - tag.getABC().restoreControlFlow(); - } - } else { - ABC abc = abcPanel.abc; - int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); - if (bi != -1) { - abc.bodies.get(bi).restoreControlFlow(abc.constants, abcPanel.decompiledTextArea.getCurrentTrait(), abc.method_info.get(abc.bodies.get(bi).method_info)); - } - - abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abc, abcPanel.decompiledTextArea.getCurrentTrait(), abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getScriptIndex()); - } - return null; - } - - @Override - protected void onStart() { - Main.startWork(translate("work.restoringControlFlow"), this); - } - - @Override - protected void done() { - View.execInEventDispatch(() -> { - Main.stopWork(); - View.showMessageDialog(null, translate("work.restoringControlFlow.complete")); - - getABCPanel().reload(); - updateClassesList(); - }); - } - }.execute(); - } - } - - public void renameIdentifiers(final SWF swf) { - if (swf == null) { - return; - } - if (confirmExperimental()) { - RenameDialog renameDialog = new RenameDialog(); - if (renameDialog.showRenameDialog() == AppDialog.OK_OPTION) { - final RenameType renameType = renameDialog.getRenameType(); - new CancellableWorker() { - @Override - protected Integer doInBackground() throws Exception { - int cnt = swf.deobfuscateIdentifiers(renameType); - return cnt; - } - - @Override - protected void onStart() { - Main.startWork(translate("work.renaming.identifiers") + "...", this); - } - - @Override - protected void done() { - View.execInEventDispatch(() -> { - try { - int cnt = get(); - Main.stopWork(); - View.showMessageDialog(null, translate("message.rename.renamed").replace("%count%", Integer.toString(cnt))); - swf.assignClassesToSymbols(); - swf.clearScriptCache(); - if (abcPanel != null) { - abcPanel.reload(); - } - updateClassesList(); - reload(true); - } catch (Exception ex) { - logger.log(Level.SEVERE, "Error during renaming identifiers", ex); - Main.stopWork(); - View.showMessageDialog(null, translate("error.occured").replace("%error%", ex.getClass().getSimpleName())); - } - }); - } - }.execute(); - } - } - } - - public void deobfuscate() { - DeobfuscationDialog deobfuscationDialog = new DeobfuscationDialog(); - if (deobfuscationDialog.showDialog() == AppDialog.OK_OPTION) { - DeobfuscationLevel level = DeobfuscationLevel.getByLevel(deobfuscationDialog.codeProcessingLevel.getValue()); - new CancellableWorker() { - @Override - protected Void doInBackground() throws Exception { - try { - ABCPanel abcPanel = getABCPanel(); - if (deobfuscationDialog.processAllCheckbox.isSelected()) { - SWF swf = abcPanel.getSwf(); - swf.deobfuscate(level); - } else { - int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); - DecompiledEditorPane decompiledTextArea = abcPanel.decompiledTextArea; - Trait t = abcPanel.decompiledTextArea.getCurrentTrait(); - ABC abc = abcPanel.abc; - if (bi != -1) { - int scriptIndex = decompiledTextArea.getScriptLeaf().scriptIndex; - int classIndex = decompiledTextArea.getClassIndex(); - boolean isStatic = decompiledTextArea.getIsStatic(); - abc.bodies.get(bi).deobfuscate(level, t, scriptIndex, classIndex, isStatic, ""/*FIXME*/); - } - - abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abc, t, abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getScriptIndex()); - } - } catch (Exception ex) { - logger.log(Level.SEVERE, "Deobfuscation error", ex); - } - - return null; - } - - @Override - protected void onStart() { - Main.startWork(translate("work.deobfuscating") + "...", this); - } - - @Override - protected void done() { - View.execInEventDispatch(() -> { - Main.stopWork(); - View.showMessageDialog(null, translate("work.deobfuscating.complete")); - - clearAllScriptCache(); - getABCPanel().reload(); - updateClassesList(); - }); - } - }.execute(); - } - } - - public void removeNonScripts(SWF swf) { - if (swf == null) { - return; - } - - List tags = new ArrayList<>(swf.tags); - List toRemove = new ArrayList<>(); - for (Tag tag : tags) { - System.out.println(tag.getClass()); - if (!(tag instanceof ABCContainerTag || tag instanceof ASMSource)) { - toRemove.add(tag); - } - } - - swf.removeTags(toRemove, true); - refreshTree(swf); - } - - private void clear() { - dumpViewPanel.clear(); - previewPanel.clear(); - headerPanel.clear(); - folderPreviewPanel.clear(); - if (abcPanel != null) { - abcPanel.clearSwf(); - } - if (actionPanel != null) { - actionPanel.clearSource(); - } - } - - public void refreshTree() { - refreshTree(new SWF[0]); - } - - public void refreshTree(SWF swf) { - refreshTree(new SWF[]{swf}); - } - - public void refreshTree(SWF[] swfs) { - clear(); - showCard(CARDEMPTYPANEL); - TreeItem treeItem = tagTree.getCurrentTreeItem(); - - tagTree.updateSwfs(swfs); - - if (treeItem != null) { - SWF treeItemSwf = treeItem.getSwf().getRootSwf(); - if (this.swfs.contains(treeItemSwf.swfList)) { - setTagTreeSelectedNode(treeItem); - } - } - - reload(true); - } - - public void refreshDecompiled() { - clearAllScriptCache(); - if (abcPanel != null) { - abcPanel.reload(); - } - - reload(true); - updateClassesList(); - } - - private MissingCharacterHandler getMissingCharacterHandler() { - return new MissingCharacterHandler() { - - // "configuration items" for the current replace only - private final ConfigurationItem showAgainIgnoreMissingCharacters = new ConfigurationItem<>("showAgainIgnoreMissingCharacters", true, true); - - private boolean ignoreMissingCharacters = false; - - @Override - public boolean getIgnoreMissingCharacters() { - return ignoreMissingCharacters; - } - - @Override - public boolean handle(TextTag textTag, final FontTag font, final char character) { - String fontName = font.getSwf().sourceFontNamesMap.get(font.getFontId()); - if (fontName == null) { - fontName = font.getFontName(); - } - final Font f = FontTag.installedFontsByName.get(fontName); - if (f == null || !f.canDisplay(character)) { - String msg = translate("error.font.nocharacter").replace("%char%", "" + character); - logger.log(Level.SEVERE, msg + " FontId: " + font.getCharacterId() + " TextId: " + textTag.getCharacterId()); - ignoreMissingCharacters = View.showConfirmDialog(null, msg, translate("error"), - JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE, - showAgainIgnoreMissingCharacters, - ignoreMissingCharacters ? JOptionPane.OK_OPTION : JOptionPane.CANCEL_OPTION) == JOptionPane.OK_OPTION; - return false; - } - - font.addCharacter(character, f); - - return true; - } - }; - } - - public boolean saveText(TextTag textTag, String formattedText, String[] texts) { - try { - if (textTag.setFormattedText(getMissingCharacterHandler(), formattedText, texts)) { - return true; - } - } catch (TextParseException ex) { - View.showMessageDialog(null, translate("error.text.invalid").replace("%text%", ex.text).replace("%line%", Long.toString(ex.line)), translate("error"), JOptionPane.ERROR_MESSAGE); - } - - return false; - } - - public boolean alignText(TextTag textTag, TextAlign textAlign) { - if (textTag.alignText(textAlign)) { - return true; - } - - return false; - } - - public boolean translateText(TextTag textTag, int diff) { - if (textTag.translateText(diff)) { - return true; - } - - return false; - } - - public boolean previousTag() { - if (getCurrentView() == VIEW_RESOURCES) { - if (tagTree.getSelectionRows().length > 0) { - int row = tagTree.getSelectionRows()[0]; - if (row > 0) { - tagTree.setSelectionRow(row - 1); - tagTree.scrollRowToVisible(row - 1); - previewPanel.focusTextPanel(); - } - } - return true; - } - return false; - } - - public boolean nextTag() { - if (getCurrentView() == VIEW_RESOURCES) { - if (tagTree.getSelectionRows().length > 0) { - int row = tagTree.getSelectionRows()[0]; - if (row < tagTree.getRowCount() - 1) { - tagTree.setSelectionRow(row + 1); - tagTree.scrollRowToVisible(row + 1); - previewPanel.focusTextPanel(); - } - } - return true; - } - return false; - } - - public void selectBkColorButtonActionPerformed(ActionEvent evt) { - Color newColor = JColorChooser.showDialog(null, AppStrings.translate("dialog.selectbkcolor.title"), View.getSwfBackgroundColor()); - if (newColor != null) { - View.setSwfBackgroundColor(newColor); - reload(true); - } - } - - public void replaceButtonActionPerformed(ActionEvent evt) { - TreeItem item = tagTree.getCurrentTreeItem(); - if (item == null) { - return; - } - - if (item instanceof DefineSoundTag) { - File selectedFile = showImportFileChooser("filter.sounds|*.mp3;*.wav|filter.sounds.mp3|*.mp3|filter.sounds.wav|*.wav"); - if (selectedFile != null) { - File selfile = Helper.fixDialogFile(selectedFile); - DefineSoundTag ds = (DefineSoundTag) item; - int soundFormat = SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN; - if (selfile.getName().toLowerCase().endsWith(".mp3")) { - soundFormat = SoundFormat.FORMAT_MP3; - } - - boolean ok = false; - try { - ok = ds.setSound(new FileInputStream(selfile), soundFormat); - } catch (IOException ex) { - //ignore - } - - if (!ok) { - View.showMessageDialog(null, translate("error.sound.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); - } else { - reload(true); - } - } - } - if (item instanceof ImageTag) { - ImageTag it = (ImageTag) item; - if (it.importSupported()) { - File selectedFile = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp"); - if (selectedFile != null) { - File selfile = Helper.fixDialogFile(selectedFile); - byte[] data = Helper.readFile(selfile.getAbsolutePath()); - try { - Tag newTag = new ImageImporter().importImage(it, data); - SWF swf = it.getSwf(); - if (newTag != null) { - refreshTree(swf); - setTagTreeSelectedNode(newTag); - } - swf.clearImageCache(); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Invalid image", ex); - View.showMessageDialog(null, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); - } - - reload(true); - } - } - } - if (item instanceof ShapeTag) { - ShapeTag st = (ShapeTag) item; - File selectedFile = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp"); - if (selectedFile != null) { - File selfile = Helper.fixDialogFile(selectedFile); - byte[] data = Helper.readFile(selfile.getAbsolutePath()); - try { - Tag newTag = new ShapeImporter().importImage(st, data); - SWF swf = st.getSwf(); - if (newTag != null) { - refreshTree(swf); - setTagTreeSelectedNode(newTag); - } - - swf.clearImageCache(); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Invalid image", ex); - View.showMessageDialog(null, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); - } - reload(true); - } - } - if (item instanceof DefineBinaryDataTag) { - DefineBinaryDataTag bt = (DefineBinaryDataTag) item; - File selectedFile = showImportFileChooser(""); - if (selectedFile != null) { - File selfile = Helper.fixDialogFile(selectedFile); - byte[] data = Helper.readFile(selfile.getAbsolutePath()); - new BinaryDataImporter().importData(bt, data); - refreshTree(bt.getSwf()); - reload(true); - } - } - } - - public void replaceAlphaButtonActionPerformed(ActionEvent evt) { - TreeItem item = tagTree.getCurrentTreeItem(); - if (item == null) { - return; - } - - if (item instanceof DefineBitsJPEG3Tag || item instanceof DefineBitsJPEG4Tag) { - ImageTag it = (ImageTag) item; - if (it.importSupported()) { - File selectedFile = showImportFileChooser(""); - if (selectedFile != null) { - File selfile = Helper.fixDialogFile(selectedFile); - byte[] data = Helper.readFile(selfile.getAbsolutePath()); - try { - new ImageImporter().importImageAlpha(it, data); - SWF swf = it.getSwf(); - swf.clearImageCache(); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Invalid alpha channel data", ex); - View.showMessageDialog(null, translate("error.image.alpha.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); - } - - reload(true); - } - } - } - } - - public void exportJavaSourceActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - exportJavaSource(); - } - - public void exportSwfXmlActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - exportSwfXml(); - } - - public void importSwfXmlActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - importSwfXml(); - } - - public void exportSelectionActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - export(true); - } - - private File showImportFileChooser(String filter) { - String[] filterArray = filter.length() > 0 ? filter.split("\\|") : new String[0]; - - JFileChooser fc = new JFileChooser(); - fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); - boolean first = true; - for (int i = 0; i < filterArray.length; i += 2) { - final String filterName = filterArray[i]; - final String[] extensions = filterArray[i + 1].split(";"); - for (int j = 0; j < extensions.length; j++) { - if (extensions[j].startsWith("*.")) { - extensions[j] = extensions[j].substring(1); - } - } - FileFilter ff = new FileFilter() { - - @Override - public boolean accept(File f) { - if (f.isDirectory()) { - return true; - } - String fileName = f.getName().toLowerCase(); - for (String ext : extensions) { - if (fileName.endsWith(ext)) { - return true; - } - } - return false; - } - - @Override - public String getDescription() { - return translate(filterName); - } - }; - if (first) { - fc.setFileFilter(ff); - } else { - fc.addChoosableFileFilter(ff); - } - first = false; - } - - JFrame f = new JFrame(); - View.setWindowIcon(f); - if (fc.showOpenDialog(f) == JFileChooser.APPROVE_OPTION) { - File result = fc.getSelectedFile(); - Configuration.lastOpenDir.set(Helper.fixDialogFile(result).getParentFile().getAbsolutePath()); - return result; - } - - return null; - } - - private void showDetail(String card) { - CardLayout cl = (CardLayout) (detailPanel.getLayout()); - cl.show(detailPanel, card); - if (card.equals(DETAILCARDEMPTYPANEL)) { - if (detailPanel.isVisible()) { - detailPanel.setVisible(false); - } - } else { - if (!detailPanel.isVisible()) { - detailPanel.setVisible(true); - } - } - } - - private void showCard(String card) { - CardLayout cl = (CardLayout) (displayPanel.getLayout()); - cl.show(displayPanel, card); - } - - @Override - public void valueChanged(TreeSelectionEvent e) { - Object source = e.getSource(); - if (source == dumpTree) { - reload(false); - return; - } - - TreeItem treeItem = (TreeItem) e.getPath().getLastPathComponent(); - if (!(treeItem instanceof SWFList)) { - SWF swf = treeItem.getSwf(); - if (swfs.isEmpty()) { - // show welcome panel after closing swfs - updateUi(); - } else { - if (swf == null) { - swf = swfs.get(0).get(0); - } - - updateUi(swf); - } - } else { - updateUi(); - } - - reload(false); - } - - public void unloadFlashPlayer() { - if (flashPanel != null) { - try { - flashPanel.close(); - } catch (IOException ex) { - // ignore - } - } - } - - private void stopFlashPlayer() { - if (flashPanel != null) { - if (!flashPanel.isStopped()) { - flashPanel.stopSWF(); - } - } - } - - public boolean isInternalFlashViewerSelected() { - return mainMenu.isInternalFlashViewerSelected(); - } - - public static final int VIEW_RESOURCES = 0; - - public static final int VIEW_DUMP = 1; - - public static final int VIEW_TIMELINE = 2; - - private int getCurrentView() { - return Configuration.dumpView.get() ? VIEW_DUMP : VIEW_RESOURCES; - } - - public void setTreeModel(int view) { - switch (view) { - case VIEW_DUMP: - if (dumpTree.getModel() == null) { - DumpTreeModel dtm = new DumpTreeModel(swfs); - dumpTree.setModel(dtm); - dumpTree.expandFirstLevelNodes(); - } - break; - case VIEW_RESOURCES: - if (tagTree.getModel() == null) { - TagTreeModel ttm = new TagTreeModel(swfs, Configuration.tagTreeShowEmptyFolders.get()); - tagTree.setModel(ttm); - tagTree.expandFirstLevelNodes(); - } - break; - } - } - - public boolean showView(int view) { - - CardLayout cl = (CardLayout) (contentPanel.getLayout()); - setTreeModel(view); - switch (view) { - case VIEW_DUMP: - if (!isWelcomeScreen) { - cl.show(contentPanel, SPLIT_PANE1); - } - treePanel.removeAll(); - treePanel.add(new JScrollPane(dumpTree), BorderLayout.CENTER); - treePanelMode = TreePanelMode.DUMP_TREE; - showDetail(DETAILCARDEMPTYPANEL); - reload(true); - treePanel.revalidate(); - return true; - case VIEW_RESOURCES: - if (!isWelcomeScreen) { - cl.show(contentPanel, SPLIT_PANE1); - } - treePanel.removeAll(); - treePanel.add(new JScrollPane(tagTree), BorderLayout.CENTER); - treePanel.add(searchPanel, BorderLayout.SOUTH); - treePanelMode = TreePanelMode.TAG_TREE; - - treePanel.addComponentListener(new ComponentAdapter() { - - @Override - public void componentResized(ComponentEvent e) { - tagTree.scrollPathToVisible(tagTree.getSelectionPath()); - } - - }); - - reload(true); - treePanel.revalidate(); - return true; - case VIEW_TIMELINE: - final SWF swf = getCurrentSwf(); - if (swf != null) { - TreeItem item = tagTree.getCurrentTreeItem(); - if (item instanceof TagScript) { - item = ((TagScript) item).getTag(); - } - if (item instanceof Timelined) { - timelineViewPanel.setTimelined((Timelined) item); - } else { - timelineViewPanel.setTimelined(swf); - } - cl.show(contentPanel, TIMELINE_PANEL); - return true; - } - return false; - } - return false; - - } - - private void dumpViewReload(boolean forceReload) { - showDetail(DETAILCARDEMPTYPANEL); - - DumpInfo dumpInfo = (DumpInfo) dumpTree.getLastSelectedPathComponent(); - if (dumpInfo == null) { - showCard(CARDEMPTYPANEL); - return; - } - - dumpViewPanel.revalidate(); - dumpViewPanel.setSelectedNode(dumpInfo); - showCard(CARDDUMPVIEW); - } - - public void loadFromBinaryTag(final DefineBinaryDataTag binaryDataTag) { - loadFromBinaryTag(Arrays.asList(binaryDataTag)); - } - - public void loadFromBinaryTag(final List binaryDataTags) { - - Main.loadingDialog.setVisible(true); - new CancellableWorker() { - - @Override - protected Void doInBackground() throws Exception { - try { - for (DefineBinaryDataTag binaryDataTag : binaryDataTags) { - try { - InputStream is = new ByteArrayInputStream(binaryDataTag.binaryData.getRangeData()); - SWF bswf = new SWF(is, null, "(SWF Data)", new ProgressListener() { - - @Override - public void progress(int p) { - Main.loadingDialog.setPercent(p); - } - }, Configuration.parallelSpeedUp.get()); - binaryDataTag.innerSwf = bswf; - bswf.binaryData = binaryDataTag; - } catch (IOException ex) { - //ignore - } - } - } catch (InterruptedException ex) { - //ignore - } - - return null; - } - - @Override - protected void onStart() { - Main.startWork(AppStrings.translate("work.reading.swf") + "...", this); - } - - @Override - protected void done() { - View.execInEventDispatch(() -> { - Main.loadingDialog.setVisible(false); - Main.stopWork(); - }); - } - }.execute(); - } - - private void closeTag() { - previewPanel.closeTag(); - } - - public void reload(boolean forceReload) { - tagTree.scrollPathToVisible(tagTree.getSelectionPath()); - if (Configuration.dumpView.get()) { - dumpViewReload(forceReload); - return; - } - - TreeItem treeItem = null; - TreePath treePath = tagTree.getSelectionPath(); - if (treePath != null && tagTree.getModel().treePathExists(treePath)) { - treeItem = (TreeItem) treePath.getLastPathComponent(); - } - - // save last selected node to config - if (treeItem != null) { - SWF swf = treeItem.getSwf(); - if (swf != null) { - swf = swf.getRootSwf(); - } - - if (swf != null) { - SwfSpecificConfiguration swfConf = Configuration.getOrCreateSwfSpecificConfiguration(swf.getShortFileName()); - swfConf.lastSelectedPath = tagTree.getSelectionPathString(); - } - } - - if (!forceReload && (treeItem == oldItem)) { - return; - } - - if (oldItem != treeItem) { - closeTag(); - } - - oldItem = treeItem; - - // show the preview of the tag when the user clicks to the tagname inside the scripts node, too - // this is a little bit inconsistent, beacuse the frames (FrameScript) are not shown - boolean preferScript = false; - if (treeItem instanceof TagScript) { - treeItem = ((TagScript) treeItem).getTag(); - preferScript = true; - } - - folderPreviewPanel.clear(); - previewPanel.clear(); - stopFlashPlayer(); - - previewPanel.setImageReplaceButtonVisible(false, false); - - boolean internalViewer = isInternalFlashViewerSelected(); - - if (treeItem instanceof ScriptPack) { - final ScriptPack scriptLeaf = (ScriptPack) treeItem; - if (setSourceWorker != null) { - setSourceWorker.cancel(true); - setSourceWorker = null; - } - if (!Main.isWorking()) { - CancellableWorker worker = new CancellableWorker() { - - @Override - protected Void doInBackground() throws Exception { - ABCPanel abcPanel = getABCPanel(); - abcPanel.detailPanel.methodTraitPanel.methodCodePanel.clear(); - abcPanel.setAbc(scriptLeaf.abc); - abcPanel.decompiledTextArea.setScript(scriptLeaf); - abcPanel.decompiledTextArea.setNoTrait(); - return null; - } - - @Override - protected void onStart() { - Main.startWork(translate("work.decompiling") + "...", this); - } - - @Override - protected void done() { - View.execInEventDispatch(() -> { - setSourceWorker = null; - try { - get(); - } catch (CancellationException ex) { - getABCPanel().decompiledTextArea.setText("// " + AppStrings.translate("work.canceled")); - } catch (Exception ex) { - Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, "Error", ex); - getABCPanel().decompiledTextArea.setText("// " + AppStrings.translate("decompilationError") + ": " + ex); - } - - Main.stopWork(); - }); - } - }; - - worker.execute(); - setSourceWorker = worker; - } - - showDetail(DETAILCARDAS3NAVIGATOR); - showCard(CARDACTIONSCRIPT3PANEL); - return; - } - - if (treeItem instanceof Tag) { - Tag tag = (Tag) treeItem; - TagInfo tagInfo = new TagInfo(); - tag.getTagInfo(tagInfo); - if (!tagInfo.isEmpty()) { - tagInfoPanel.setTagInfos(tagInfo); - showDetail(DETAILCARDTAGINFO); - } else { - showDetail(DETAILCARDEMPTYPANEL); - } - } else { - showDetail(DETAILCARDEMPTYPANEL); - } - - if (treeItem instanceof HeaderItem) { - headerPanel.load(((HeaderItem) treeItem).getSwf()); - showCard(CARDHEADER); - } else if (treeItem instanceof FolderItem) { - showFolderPreview((FolderItem) treeItem); - } else if (treeItem instanceof SWF) { - SWF swf = (SWF) treeItem; - if (internalViewer) { - previewPanel.showImagePanel(swf, swf, -1); - } else { - previewPanel.setParametersPanelVisible(false); - if (flashPanel != null) { - previewPanel.showFlashViewerPanel(); - previewPanel.showSwf(swf); - } - } - - showCard(CARDPREVIEWPANEL); - } else if (treeItem instanceof MetadataTag) { - MetadataTag metadataTag = (MetadataTag) treeItem; - previewPanel.showMetaDataPanel(metadataTag); - showCard(CARDPREVIEWPANEL); - } else if (treeItem instanceof DefineBinaryDataTag) { - DefineBinaryDataTag binaryTag = (DefineBinaryDataTag) treeItem; - previewPanel.showBinaryPanel(binaryTag); - showCard(CARDPREVIEWPANEL); - } else if (treeItem instanceof ASMSource && (!(treeItem instanceof DrawableTag) || preferScript)) { - getActionPanel().setSource((ASMSource) treeItem, !forceReload); - showCard(CARDACTIONSCRIPTPANEL); - } else if (treeItem instanceof ImageTag) { - ImageTag imageTag = (ImageTag) treeItem; - previewPanel.setImageReplaceButtonVisible(imageTag.importSupported(), imageTag instanceof DefineBitsJPEG3Tag || imageTag instanceof DefineBitsJPEG4Tag); - previewPanel.showImagePanel(imageTag.getImage()); - showCard(CARDPREVIEWPANEL); - } else if ((treeItem instanceof DrawableTag) && (!(treeItem instanceof TextTag)) && (!(treeItem instanceof FontTag)) && internalViewer) { - final Tag tag = (Tag) treeItem; - DrawableTag d = (DrawableTag) tag; - Timelined timelined; - if (treeItem instanceof Timelined && !(treeItem instanceof ButtonTag)) { - timelined = (Timelined) tag; - } else { - timelined = makeTimelined(tag); - } - - previewPanel.setParametersPanelVisible(false); - previewPanel.showImagePanel(timelined, tag.getSwf(), -1); - showCard(CARDPREVIEWPANEL); - } else if ((treeItem instanceof FontTag) && internalViewer) { - showFontTag((FontTag) treeItem); - } else if ((treeItem instanceof TextTag) && internalViewer) { - showTextTag((TextTag) treeItem); - } else if (treeItem instanceof Frame && internalViewer) { - Frame fn = (Frame) treeItem; - SWF swf = fn.getSwf(); - List controlTags = swf.tags; - int containerId = 0; - RECT rect = swf.displayRect; - int totalFrameCount = swf.frameCount; - Timelined timelined = swf; - if (fn.timeline.timelined instanceof DefineSpriteTag) { - DefineSpriteTag parentSprite = (DefineSpriteTag) fn.timeline.timelined; - controlTags = parentSprite.subTags; - containerId = parentSprite.spriteId; - rect = parentSprite.getRect(); - totalFrameCount = parentSprite.frameCount; - timelined = parentSprite; - } - - previewPanel.showImagePanel(timelined, swf, fn.frame); - showCard(CARDPREVIEWPANEL); - } else if ((treeItem instanceof SoundTag)) { //&& isInternalFlashViewerSelected() && (Arrays.asList("mp3", "wav").contains(((SoundTag) tagObj).getExportFormat())))) { - previewPanel.showImagePanel(new SerializableImage(View.loadImage("sound32"))); - previewPanel.setImageReplaceButtonVisible(treeItem instanceof DefineSoundTag, false); - try { - SoundTagPlayer soundThread = new SoundTagPlayer((SoundTag) treeItem, Configuration.loopMedia.get() ? Integer.MAX_VALUE : 1, true); - previewPanel.setMedia(soundThread); - } catch (LineUnavailableException | IOException | UnsupportedAudioFileException ex) { - logger.log(Level.SEVERE, null, ex); - } - - showCard(CARDPREVIEWPANEL); - } else if ((treeItem instanceof Frame) || ((treeItem instanceof CharacterTag) || (treeItem instanceof FontTag)) && (treeItem instanceof Tag) || (treeItem instanceof SoundStreamHeadTypeTag)) { - previewPanel.createAndShowTempSwf(treeItem); - - if (treeItem instanceof TextTag) { - showTextTag((TextTag) treeItem); - } else if (treeItem instanceof FontTag) { - showFontTag((FontTag) treeItem); - } else { - previewPanel.setParametersPanelVisible(false); - } - - showCard(CARDPREVIEWPANEL); - } else if (treeItem instanceof Tag) { - showGenericTag((Tag) treeItem); - } else { - showCard(CARDEMPTYPANEL); - } - } - - public void repaintTree() { - tagTree.repaint(); - reload(true); - } - - public void showGenericTag(Tag tag) { - previewPanel.showGenericTagPanel(tag); - showCard(CARDPREVIEWPANEL); - } - - private void showFontTag(FontTag ft) { - - previewPanel.showFontPanel(ft); - showCard(CARDPREVIEWPANEL); - } - - private void showTextTag(TextTag textTag) { - - previewPanel.showTextPanel(textTag); - showCard(CARDPREVIEWPANEL); - } - - public void showTextTagWithNewValue(TextTag textTag, TextTag newTextTag) { - - previewPanel.showTextComparePanel(textTag, newTextTag); - } - - private void showFolderPreview(FolderItem item) { - List folderPreviewItems = new ArrayList<>(); - String folderName = item.getName(); - SWF swf = item.swf; - switch (folderName) { - case TagTreeModel.FOLDER_SHAPES: - for (Tag tag : swf.tags) { - if (tag instanceof ShapeTag) { - folderPreviewItems.add(tag); - } - } - break; - case TagTreeModel.FOLDER_MORPHSHAPES: - for (Tag tag : swf.tags) { - if (tag instanceof MorphShapeTag) { - folderPreviewItems.add(tag); - } - } - break; - case TagTreeModel.FOLDER_SPRITES: - for (Tag tag : swf.tags) { - if (tag instanceof DefineSpriteTag) { - folderPreviewItems.add(tag); - } - } - break; - case TagTreeModel.FOLDER_BUTTONS: - for (Tag tag : swf.tags) { - if (tag instanceof ButtonTag) { - folderPreviewItems.add(tag); - } - } - break; - case TagTreeModel.FOLDER_FONTS: - for (Tag tag : swf.tags) { - if (tag instanceof FontTag) { - folderPreviewItems.add(tag); - } - } - break; - case TagTreeModel.FOLDER_FRAMES: - for (Frame frame : swf.getTimeline().getFrames()) { - folderPreviewItems.add(frame); - } - break; - case TagTreeModel.FOLDER_IMAGES: - for (Tag tag : swf.tags) { - if (tag instanceof ImageTag) { - folderPreviewItems.add(tag); - } - } - break; - case TagTreeModel.FOLDER_TEXTS: - for (Tag tag : swf.tags) { - if (tag instanceof TextTag) { - folderPreviewItems.add(tag); - } - } - break; - } - - folderPreviewPanel.setItems(folderPreviewItems); - showCard(CARDFOLDERPREVIEWPANEL); - } - - private boolean isFreeing; - - @Override - public boolean isFreeing() { - return isFreeing; - } - - @Override - public void free() { - isFreeing = true; - } - - public void setErrorState(ErrorState errorState) { - statusPanel.setErrorState(errorState); - } - - public static Timelined makeTimelined(final Tag tag) { - return makeTimelined(tag, -1); - } - - public static Timelined makeTimelined(final Tag tag, final int fontFrameNum) { - - return new Timelined() { - - private Timeline tim; - - @Override - public Timeline getTimeline() { - if (tim != null) { - return tim; - } - tim = new Timeline(tag.getSwf(), null, new ArrayList<>(), ((CharacterTag) tag).getCharacterId(), getRect()); - initTimeline(tim); - return tim; - } - - @Override - public void resetTimeline() { - if (tim != null) { - tim.reset(tag.getSwf(), null, new ArrayList<>(), ((CharacterTag) tag).getCharacterId(), getRect()); - initTimeline(tim); - } - } - - private void initTimeline(Timeline timeline) { - if (tag instanceof MorphShapeTag) { - tim.frameRate = MORPH_SHAPE_ANIMATION_FRAME_RATE; - int framesCnt = tim.frameRate * MORPH_SHAPE_ANIMATION_LENGTH; - for (int i = 0; i < framesCnt; i++) { - Frame f = new Frame(tim, i); - DepthState ds = new DepthState(tag.getSwf(), f); - ds.characterId = ((CharacterTag) tag).getCharacterId(); - ds.matrix = new MATRIX(); - ds.ratio = i * 65535 / framesCnt; - f.layers.put(1, ds); - f.layersChanged = true; - tim.addFrame(f); - } - } else if (tag instanceof FontTag) { - int pageCount = PreviewPanel.getFontPageCount((FontTag) tag); - int frame = fontFrameNum; - if (frame < 0 || frame >= pageCount) { - frame = 0; - } - - Frame f = new Frame(tim, 0); - DepthState ds = new DepthState(tag.getSwf(), f); - ds.characterId = ((CharacterTag) tag).getCharacterId(); - ds.matrix = new MATRIX(); - f.layers.put(1, ds); - f.layersChanged = true; - tim.addFrame(f); - tim.fontFrameNum = frame; - } else { - Frame f = new Frame(tim, 0); - DepthState ds = new DepthState(tag.getSwf(), f); - ds.characterId = ((CharacterTag) tag).getCharacterId(); - ds.matrix = new MATRIX(); - f.layers.put(1, ds); - tim.addFrame(f); - } - tim.displayRect = getRect(); - } - - @Override - public RECT getRect() { - return getRect(new HashSet<>()); - } - - @Override - public RECT getRect(Set added) { - BoundedTag bt = (BoundedTag) tag; - if (!added.contains(bt)) { - return bt.getRect(added); - } - return new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); - } - - @Override - public int hashCode() { - return tag.hashCode(); - } - }; - } - - private void disposeInner(Container container) { - for (Component c : container.getComponents()) { - if (c instanceof Container) { - Container c2 = (Container) c; - disposeInner(c2); - } - } - - container.removeAll(); - container.setLayout(null); - if (container instanceof TagEditorPanel) { - Helper.emptyObject(container); - } - } - - public void dispose() { - setDropTarget(null); - disposeInner(this); - Helper.emptyObject(this); - } -} +/* + * Copyright (C) 2010-2015 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.AbortRetryIgnoreHandler; +import com.jpexs.decompiler.flash.ApplicationInfo; +import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFBundle; +import com.jpexs.decompiler.flash.SWFSourceInfo; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.RenameType; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.DeobfuscationLevel; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.configuration.ConfigurationItem; +import com.jpexs.decompiler.flash.configuration.SwfSpecificConfiguration; +import com.jpexs.decompiler.flash.dumpview.DumpInfo; +import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode; +import com.jpexs.decompiler.flash.exporters.BinaryDataExporter; +import com.jpexs.decompiler.flash.exporters.FontExporter; +import com.jpexs.decompiler.flash.exporters.FrameExporter; +import com.jpexs.decompiler.flash.exporters.ImageExporter; +import com.jpexs.decompiler.flash.exporters.MorphShapeExporter; +import com.jpexs.decompiler.flash.exporters.MovieExporter; +import com.jpexs.decompiler.flash.exporters.ShapeExporter; +import com.jpexs.decompiler.flash.exporters.SoundExporter; +import com.jpexs.decompiler.flash.exporters.SymbolClassExporter; +import com.jpexs.decompiler.flash.exporters.TextExporter; +import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode; +import com.jpexs.decompiler.flash.exporters.modes.ButtonExportMode; +import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; +import com.jpexs.decompiler.flash.exporters.modes.FrameExportMode; +import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode; +import com.jpexs.decompiler.flash.exporters.modes.MorphShapeExportMode; +import com.jpexs.decompiler.flash.exporters.modes.MovieExportMode; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode; +import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode; +import com.jpexs.decompiler.flash.exporters.modes.SpriteExportMode; +import com.jpexs.decompiler.flash.exporters.modes.SymbolClassExportMode; +import com.jpexs.decompiler.flash.exporters.modes.TextExportMode; +import com.jpexs.decompiler.flash.exporters.script.AS2ScriptExporter; +import com.jpexs.decompiler.flash.exporters.script.AS3ScriptExporter; +import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.ButtonExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.FrameExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.ImageExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.MorphShapeExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.MovieExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.ShapeExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.SpriteExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings; +import com.jpexs.decompiler.flash.exporters.swf.SwfJavaExporter; +import com.jpexs.decompiler.flash.exporters.swf.SwfXmlExporter; +import com.jpexs.decompiler.flash.gui.abc.ABCPanel; +import com.jpexs.decompiler.flash.gui.abc.ABCPanelSearchResult; +import com.jpexs.decompiler.flash.gui.abc.ClassesListTreeModel; +import com.jpexs.decompiler.flash.gui.abc.DecompiledEditorPane; +import com.jpexs.decompiler.flash.gui.abc.DeobfuscationDialog; +import com.jpexs.decompiler.flash.gui.action.ActionPanel; +import com.jpexs.decompiler.flash.gui.action.ActionSearchResult; +import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; +import com.jpexs.decompiler.flash.gui.dumpview.DumpTree; +import com.jpexs.decompiler.flash.gui.dumpview.DumpTreeModel; +import com.jpexs.decompiler.flash.gui.dumpview.DumpViewPanel; +import com.jpexs.decompiler.flash.gui.helpers.ObservableList; +import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; +import com.jpexs.decompiler.flash.gui.tagtree.TagTree; +import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; +import com.jpexs.decompiler.flash.gui.timeline.TimelineViewPanel; +import com.jpexs.decompiler.flash.helpers.FileTextWriter; +import com.jpexs.decompiler.flash.helpers.Freed; +import com.jpexs.decompiler.flash.importers.AS2ScriptImporter; +import com.jpexs.decompiler.flash.importers.AS3ScriptImporter; +import com.jpexs.decompiler.flash.importers.BinaryDataImporter; +import com.jpexs.decompiler.flash.importers.ImageImporter; +import com.jpexs.decompiler.flash.importers.ShapeImporter; +import com.jpexs.decompiler.flash.importers.SwfXmlImporter; +import com.jpexs.decompiler.flash.importers.SymbolClassImporter; +import com.jpexs.decompiler.flash.importers.TextImporter; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; +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.FileAttributesTag; +import com.jpexs.decompiler.flash.tags.MetadataTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.TagInfo; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.DrawableTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; +import com.jpexs.decompiler.flash.tags.base.SoundTag; +import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag; +import com.jpexs.decompiler.flash.tags.base.TextImportErrorHandler; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.text.TextAlign; +import com.jpexs.decompiler.flash.tags.text.TextParseException; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.timeline.TagScript; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.FolderItem; +import com.jpexs.decompiler.flash.treeitems.HeaderItem; +import com.jpexs.decompiler.flash.treeitems.SWFList; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.sound.SoundFormat; +import com.jpexs.decompiler.flash.xfl.FLAVersion; +import com.jpexs.helpers.CancellableWorker; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.Path; +import com.jpexs.helpers.ProgressListener; +import com.jpexs.helpers.SerializableImage; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Desktop; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.event.ActionEvent; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.UnsupportedAudioFileException; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.Icon; +import javax.swing.JColorChooser; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.SwingConstants; +import javax.swing.UIManager; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.filechooser.FileFilter; +import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.tree.DefaultTreeSelectionModel; +import javax.swing.tree.TreePath; +import jsyntaxpane.DefaultSyntaxKit; + +/** + * + * @author JPEXS + */ +public final class MainPanel extends JPanel implements TreeSelectionListener, SearchListener, Freed { + + private final MainFrame mainFrame; + + private final ObservableList swfs; + + private final JPanel welcomePanel; + + private final TimelineViewPanel timelineViewPanel; + + private final MainFrameStatusPanel statusPanel; + + private final MainFrameMenu mainMenu; + + private final JProgressBar progressBar = new JProgressBar(0, 100); + + public TagTree tagTree; + + public DumpTree dumpTree; + + private final FlashPlayerPanel flashPanel; + + private final JPanel contentPanel; + + private final JPanel displayPanel; + + public FolderPreviewPanel folderPreviewPanel; + + private boolean isWelcomeScreen = true; + + private static final String CARDPREVIEWPANEL = "Preview card"; + + private static final String CARDFOLDERPREVIEWPANEL = "Folder preview card"; + + private static final String CARDEMPTYPANEL = "Empty card"; + + private static final String CARDDUMPVIEW = "Dump view"; + + private static final String CARDACTIONSCRIPTPANEL = "ActionScript card"; + + private static final String CARDACTIONSCRIPT3PANEL = "ActionScript3 card"; + + private static final String CARDHEADER = "Header card"; + + private static final String DETAILCARDAS3NAVIGATOR = "Traits list"; + + private static final String DETAILCARDTAGINFO = "Tag information"; + + private static final String DETAILCARDEMPTYPANEL = "Empty card"; + + private static final String SPLIT_PANE1 = "SPLITPANE1"; + + private static final String WELCOME_PANEL = "WELCOMEPANEL"; + + private static final String TIMELINE_PANEL = "TIMELINEPANEL"; + + private final JPersistentSplitPane splitPane1; + + private final JPersistentSplitPane splitPane2; + + private JPanel detailPanel; + + private JTextField filterField = new MyTextField(""); + + private JPanel searchPanel; + + private ABCPanel abcPanel; + + private ActionPanel actionPanel; + + private final PreviewPanel previewPanel; + + private final HeaderInfoPanel headerPanel; + + private DumpViewPanel dumpViewPanel; + + private final JPanel treePanel; + + private final TagInfoPanel tagInfoPanel; + + private TreePanelMode treePanelMode; + + private CancellableWorker setSourceWorker; + + public TreeItem oldItem; + + // play morph shape in 2 second(s) + public static final int MORPH_SHAPE_ANIMATION_LENGTH = 2; + + public static final int MORPH_SHAPE_ANIMATION_FRAME_RATE = 30; + + private static final Logger logger = Logger.getLogger(MainPanel.class.getName()); + + public void setPercent(int percent) { + progressBar.setValue(percent); + progressBar.setVisible(true); + } + + public void hidePercent() { + if (progressBar.isVisible()) { + progressBar.setVisible(false); + } + } + + public MainFrame getMainFrame() { + return mainFrame; + } + + static { + try { + File.createTempFile("temp", ".swf").delete(); //First call to this is slow, so make it first + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + private static void addTab(JTabbedPane tabbedPane, Component tab, String title, Icon icon) { + tabbedPane.add(tab); + + JLabel lbl = new JLabel(title); + lbl.setIcon(icon); + lbl.setIconTextGap(5); + lbl.setHorizontalTextPosition(SwingConstants.RIGHT); + + tabbedPane.setTabComponentAt(tabbedPane.getTabCount() - 1, lbl); + } + + public void setStatus(String s) { + statusPanel.setStatus(s); + } + + public void setWorkStatus(String s, CancellableWorker worker) { + statusPanel.setWorkStatus(s, worker); + mainMenu.updateComponents(); + } + + private JPanel createWelcomePanel() { + JPanel welcomePanel = new JPanel(); + welcomePanel.setLayout(new BoxLayout(welcomePanel, BoxLayout.Y_AXIS)); + JLabel welcomeToLabel = new JLabel(translate("startup.welcometo")); + welcomeToLabel.setFont(welcomeToLabel.getFont().deriveFont(40)); + welcomeToLabel.setAlignmentX(0.5f); + JPanel appNamePanel = new JPanel(new FlowLayout()); + JLabel jpLabel = new JLabel("JPEXS "); + jpLabel.setAlignmentX(0.5f); + jpLabel.setForeground(new Color(0, 0, 160)); + jpLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); + jpLabel.setHorizontalAlignment(SwingConstants.CENTER); + appNamePanel.add(jpLabel); + + JLabel ffLabel = new JLabel("Free Flash "); + ffLabel.setAlignmentX(0.5f); + ffLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); + ffLabel.setHorizontalAlignment(SwingConstants.CENTER); + appNamePanel.add(ffLabel); + + JLabel decLabel = new JLabel("Decompiler"); + decLabel.setAlignmentX(0.5f); + decLabel.setForeground(Color.red); + decLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); + decLabel.setHorizontalAlignment(SwingConstants.CENTER); + appNamePanel.add(decLabel); + appNamePanel.setAlignmentX(0.5f); + welcomePanel.add(Box.createGlue()); + welcomePanel.add(welcomeToLabel); + welcomePanel.add(appNamePanel); + JLabel startLabel = new JLabel(translate("startup.selectopen")); + startLabel.setAlignmentX(0.5f); + startLabel.setFont(startLabel.getFont().deriveFont(30)); + welcomePanel.add(startLabel); + welcomePanel.add(Box.createGlue()); + return welcomePanel; + } + + private JPanel createFolderPreviewCard() { + JPanel folderPreviewCard = new JPanel(new BorderLayout()); + folderPreviewPanel = new FolderPreviewPanel(this, new ArrayList<>()); + folderPreviewCard.add(new JScrollPane(folderPreviewPanel), BorderLayout.CENTER); + + return folderPreviewCard; + } + + private JPanel createDumpPreviewCard() { + JPanel dumpViewCard = new JPanel(new BorderLayout()); + dumpViewPanel = new DumpViewPanel(dumpTree); + dumpViewCard.add(new JScrollPane(dumpViewPanel), BorderLayout.CENTER); + + return dumpViewCard; + } + + public String translate(String key) { + return mainFrame.translate(key); + } + + public MainPanel(MainFrame mainFrame, MainFrameMenu mainMenu, FlashPlayerPanel flashPanel) { + super(); + + this.mainFrame = mainFrame; + this.mainMenu = mainMenu; + this.flashPanel = flashPanel; + + mainFrame.setTitle(ApplicationInfo.applicationVerName); + + setLayout(new BorderLayout()); + swfs = new ObservableList<>(); + + detailPanel = new JPanel(); + detailPanel.setLayout(new CardLayout()); + + JPanel whitePanel = new JPanel(); + whitePanel.setBackground(Color.white); + detailPanel.add(whitePanel, DETAILCARDEMPTYPANEL); + + tagInfoPanel = new TagInfoPanel(this); + detailPanel.add(tagInfoPanel, DETAILCARDTAGINFO); + + UIManager.getDefaults().put("TreeUI", BasicTreeUI.class.getName()); + tagTree = new TagTree(null, this); + tagTree.addTreeSelectionListener(this); + tagTree.setSelectionModel(new DefaultTreeSelectionModel() { + + private boolean isModified() { + if (abcPanel != null && abcPanel.isEditing()) { + abcPanel.tryAutoSave(); + } + + if (actionPanel != null && actionPanel.isEditing()) { + actionPanel.tryAutoSave(); + } + + if (previewPanel.isEditing()) { + previewPanel.tryAutoSave(); + } + + if (headerPanel.isEditing()) { + headerPanel.tryAutoSave(); + } + + return (abcPanel != null && abcPanel.isEditing()) + || (actionPanel != null && actionPanel.isEditing()) + || previewPanel.isEditing() || headerPanel.isEditing(); + } + + @Override + public void addSelectionPath(TreePath path) { + if (isModified()) { + return; + } + + super.addSelectionPath(path); + } + + @Override + public void addSelectionPaths(TreePath[] paths) { + if (isModified()) { + return; + } + + super.addSelectionPaths(paths); + } + + @Override + public void setSelectionPath(TreePath path) { + if (isModified()) { + return; + } + + super.setSelectionPath(path); + } + + @Override + public void setSelectionPaths(TreePath[] pPaths) { + if (isModified()) { + return; + } + + super.setSelectionPaths(pPaths); + } + + @Override + public void clearSelection() { + if (isModified()) { + return; + } + + super.clearSelection(); + } + + public void setSelection(TreePath[] selection) { + if (isModified()) { + return; + } + + this.selection = selection; + } + + @Override + public void removeSelectionPath(TreePath path) { + if (isModified()) { + return; + } + + super.removeSelectionPath(path); + } + + @Override + public void removeSelectionPaths(TreePath[] paths) { + if (isModified()) { + return; + } + + super.removeSelectionPaths(paths); + } + }); + + DragSource dragSource = DragSource.getDefaultDragSource(); + dragSource.createDefaultDragGestureRecognizer(tagTree, DnDConstants.ACTION_COPY_OR_MOVE, new DragGestureListener() { + @Override + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(DragSource.DefaultCopyDrop, new Transferable() { + @Override + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[]{DataFlavor.javaFileListFlavor}; + } + + @Override + public boolean isDataFlavorSupported(DataFlavor flavor) { + return flavor.equals(DataFlavor.javaFileListFlavor); + } + + @Override + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { + if (flavor.equals(DataFlavor.javaFileListFlavor)) { + List files; + String tempDir = System.getProperty("java.io.tmpdir"); + if (!tempDir.endsWith(File.separator)) { + tempDir += File.separator; + } + Random rnd = new Random(); + tempDir += "ffdec" + File.separator + "export" + File.separator + System.currentTimeMillis() + "_" + rnd.nextInt(1000); + File fTempDir = new File(tempDir); + Path.createDirectorySafe(fTempDir); + + File ftemp = new File(tempDir); + ExportDialog exd = new ExportDialog(null); + try { + files = exportSelection(new GuiAbortRetryIgnoreHandler(), tempDir, exd); + } catch (InterruptedException ex) { + Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex); + return null; + } + + files.clear(); + + File[] fs = ftemp.listFiles(); + files.addAll(Arrays.asList(fs)); + + Main.stopWork(); + + for (File f : files) { + f.deleteOnExit(); + } + new File(tempDir).deleteOnExit(); + return files; + + } + return null; + } + }, new DragSourceListener() { + @Override + public void dragEnter(DragSourceDragEvent dsde) { + enableDrop(false); + } + + @Override + public void dragOver(DragSourceDragEvent dsde) { + } + + @Override + public void dropActionChanged(DragSourceDragEvent dsde) { + } + + @Override + public void dragExit(DragSourceEvent dse) { + } + + @Override + public void dragDropEnd(DragSourceDropEvent dsde) { + enableDrop(true); + } + }); + } + }); + + tagTree.createContextMenu(); + + dumpTree = new DumpTree(null, this); + dumpTree.addTreeSelectionListener(this); + dumpTree.createContextMenu(); + + statusPanel = new MainFrameStatusPanel(this); + add(statusPanel, BorderLayout.SOUTH); + + displayPanel = new JPanel(new CardLayout()); + + DefaultSyntaxKit.initKit(); + previewPanel = new PreviewPanel(this, flashPanel); + displayPanel.add(previewPanel, CARDPREVIEWPANEL); + displayPanel.add(createFolderPreviewCard(), CARDFOLDERPREVIEWPANEL); + displayPanel.add(createDumpPreviewCard(), CARDDUMPVIEW); + + headerPanel = new HeaderInfoPanel(); + displayPanel.add(headerPanel, CARDHEADER); + + displayPanel.add(new JPanel(), CARDEMPTYPANEL); + showCard(CARDEMPTYPANEL); + + searchPanel = new JPanel(); + searchPanel.setLayout(new BorderLayout()); + searchPanel.add(filterField, BorderLayout.CENTER); + searchPanel.add(new JLabel(View.getIcon("search16")), BorderLayout.WEST); + JLabel closeSearchButton = new JLabel(View.getIcon("cancel16")); + closeSearchButton.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + filterField.setText(""); + doFilter(); + searchPanel.setVisible(false); + } + }); + searchPanel.add(closeSearchButton, BorderLayout.EAST); + treePanel = new JPanel(new BorderLayout()); + treePanel.add(searchPanel, BorderLayout.SOUTH); + + searchPanel.setVisible(false); + + filterField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void changedUpdate(DocumentEvent e) { + warn(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + warn(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + warn(); + } + + public void warn() { + doFilter(); + } + }); + + //displayPanel.setBorder(BorderFactory.createLineBorder(Color.black)); + splitPane2 = new JPersistentSplitPane(JSplitPane.VERTICAL_SPLIT, treePanel, detailPanel, Configuration.guiSplitPane2DividerLocationPercent); + splitPane1 = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, splitPane2, displayPanel, Configuration.guiSplitPane1DividerLocationPercent); + + welcomePanel = createWelcomePanel(); + add(welcomePanel, BorderLayout.CENTER); + + timelineViewPanel = new TimelineViewPanel(); + + CardLayout cl3 = new CardLayout(); + contentPanel = new JPanel(cl3); + contentPanel.add(welcomePanel, WELCOME_PANEL); + contentPanel.add(splitPane1, SPLIT_PANE1); + contentPanel.add(timelineViewPanel, TIMELINE_PANEL); + add(contentPanel); + cl3.show(contentPanel, WELCOME_PANEL); + + tagTree.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if ((e.getKeyCode() == 'F') && (e.isControlDown())) { + searchPanel.setVisible(true); + filterField.requestFocusInWindow(); + } + } + }); + detailPanel.setVisible(false); + + updateUi(); + + this.swfs.addCollectionChangedListener((e) -> { + TagTreeModel ttm = tagTree.getModel(); + if (ttm != null) { + if (getCurrentSwf() == null) { + tagTree.setSelectionPath(ttm.getTreePath(ttm.getRoot())); + } + ttm.updateSwfs(e); + tagTree.expandRoot(); + tagTree.expandFirstLevelNodes(); + } + + DumpTreeModel dtm = dumpTree.getModel(); + if (dtm != null) { + List> expandedNodes = View.getExpandedNodes(dumpTree); + dtm.updateSwfs(); + View.expandTreeNodes(dumpTree, expandedNodes); + dumpTree.expandRoot(); + dumpTree.expandFirstLevelNodes(); + } + + if (swfs.isEmpty()) { + tagTree.setUI(new BasicTreeUI() { + { + setHashColor(Color.gray); + } + }); + dumpTree.setUI(new BasicTreeUI() { + { + setHashColor(Color.gray); + } + }); + } + }); + + //Opening files with drag&drop to main window + enableDrop(true); + } + + public void load(SWFList newSwfs, boolean first) { + + previewPanel.clear(); + + for (SWF swf : newSwfs) { + + if (Configuration.autoRenameIdentifiers.get()) { + try { + swf.deobfuscateIdentifiers(RenameType.TYPENUMBER); + swf.assignClassesToSymbols(); + swf.clearScriptCache(); + if (abcPanel != null) { + abcPanel.reload(); + } + + updateClassesList(); + } catch (InterruptedException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + + swfs.add(newSwfs); + SWF swf = newSwfs.size() > 0 ? newSwfs.get(0) : null; + if (swf != null) { + updateUi(swf); + } + + reload(false); + } + + private ABCPanel getABCPanel() { + if (abcPanel == null) { + abcPanel = new ABCPanel(this); + displayPanel.add(abcPanel, CARDACTIONSCRIPT3PANEL); + detailPanel.add(abcPanel.tabbedPane, DETAILCARDAS3NAVIGATOR); + } + + return abcPanel; + } + + private ActionPanel getActionPanel() { + if (actionPanel == null) { + actionPanel = new ActionPanel(MainPanel.this); + displayPanel.add(actionPanel, CARDACTIONSCRIPTPANEL); + } + + return actionPanel; + } + + private void updateUi(final SWF swf) { + + mainFrame.setTitle(ApplicationInfo.applicationVerName + (Configuration.displayFileName.get() ? " - " + swf.getFileTitle() : "")); + + List abcList = swf.getAbcList(); + + boolean hasAbc = !abcList.isEmpty(); + + if (hasAbc) { + getABCPanel().setAbc(abcList.get(0).getABC()); + } + + if (isWelcomeScreen) { + CardLayout cl = (CardLayout) (contentPanel.getLayout()); + cl.show(contentPanel, SPLIT_PANE1); + isWelcomeScreen = false; + } + + mainMenu.updateComponents(swf); + } + + private void updateUi() { + if (!isWelcomeScreen && swfs.isEmpty()) { + CardLayout cl = (CardLayout) (contentPanel.getLayout()); + cl.show(contentPanel, WELCOME_PANEL); + isWelcomeScreen = true; + } + + mainFrame.setTitle(ApplicationInfo.applicationVerName); + mainMenu.updateComponents(null); + + showView(getCurrentView()); + } + + private boolean closeConfirmation(SWFList swfList) { + if (swfList == null) { + if (View.showConfirmDialog(this, translate("message.confirm.closeAll"), translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, Configuration.showCloseConfirmation, JOptionPane.OK_OPTION) != JOptionPane.OK_OPTION) { + return false; + } + } else { + if (View.showConfirmDialog(this, translate("message.confirm.close").replace("{swfName}", swfList.toString()), translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, Configuration.showCloseConfirmation, JOptionPane.OK_OPTION) != JOptionPane.OK_OPTION) { + return false; + } + } + + return true; + } + + public boolean closeAll() { + boolean modified = false; + for (SWFList swfList : swfs) { + for (SWF swf : swfList) { + if (swf.isModified()) { + modified = true; + } + } + } + + if (modified) { + boolean closeConfirmResult = closeConfirmation(swfs.size() == 1 ? swfs.get(0) : null); + if (!closeConfirmResult) { + return false; + } + } + + List swfsLists = new ArrayList<>(swfs); + swfs.clear(); + oldItem = null; + clear(); + updateUi(); + + for (SWFList swfList : swfsLists) { + List swfs2 = new ArrayList<>(swfList); + for (SWF swf : swfs2) { + swf.clearTagSwfs(); + } + } + + return true; + } + + public boolean close(SWFList swfList) { + boolean modified = false; + for (SWF swf : swfList) { + if (swf.isModified()) { + modified = true; + } + } + + if (modified) { + boolean closeConfirmResult = closeConfirmation(swfList); + if (!closeConfirmResult) { + return false; + } + } + + swfs.remove(swfList); + oldItem = null; + clear(); + updateUi(); + + List swfs2 = new ArrayList<>(swfList); + for (SWF swf : swfs2) { + swf.clearTagSwfs(); + } + + return true; + } + + public void enableDrop(boolean value) { + if (value) { + setDropTarget(new DropTarget() { + @Override + public synchronized void drop(DropTargetDropEvent dtde) { + try { + dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); + @SuppressWarnings("unchecked") + List droppedFiles = (List) dtde.getTransferable().getTransferData(DataFlavor.javaFileListFlavor); + if (!droppedFiles.isEmpty()) { + SWFSourceInfo[] sourceInfos = new SWFSourceInfo[droppedFiles.size()]; + for (int i = 0; i < droppedFiles.size(); i++) { + sourceInfos[i] = new SWFSourceInfo(null, droppedFiles.get(i).getAbsolutePath(), null); + } + Main.openFile(sourceInfos, null); + } + } catch (UnsupportedFlavorException | IOException ex) { + } + } + }); + } else { + setDropTarget(null); + } + } + + public void updateClassesList() { + List nodes = getASTreeNodes(tagTree); + boolean updateNeeded = false; + for (TreeItem n : nodes) { + if (n instanceof ClassesListTreeModel) { + ((ClassesListTreeModel) n).update(); + updateNeeded = true; + } + } + + refreshTree(); + + if (updateNeeded) { + tagTree.updateUI(); + } + } + + public void doFilter() { + List nodes = getASTreeNodes(tagTree); + for (TreeItem n : nodes) { + if (n instanceof ClassesListTreeModel) { + ((ClassesListTreeModel) n).setFilter(filterField.getText()); + TagTreeModel tm = tagTree.getModel(); + TreePath path = tm.getTreePath(n); + tm.updateNode(path); + View.expandTreeNodes(tagTree, path, true); + } + } + } + + public void renameIdentifier(SWF swf, String identifier) throws InterruptedException { + String oldName = identifier; + String newName = View.showInputDialog(translate("rename.enternew"), oldName); + if (newName != null) { + if (!oldName.equals(newName)) { + swf.renameAS2Identifier(oldName, newName); + View.showMessageDialog(null, translate("rename.finished.identifier")); + updateClassesList(); + reload(true); + } + } + } + + public void renameMultiname(List abcList, int multiNameIndex) { + String oldName = ""; + AVM2ConstantPool constants = getABCPanel().abc.constants; + if (constants.getMultiname(multiNameIndex).name_index > 0) { + oldName = constants.getString(constants.getMultiname(multiNameIndex).name_index); + } + + String newName = View.showInputDialog(translate("rename.enternew"), oldName); + if (newName != null) { + if (!oldName.equals(newName)) { + int mulCount = 0; + for (ABCContainerTag cnt : abcList) { + ABC abc = cnt.getABC(); + for (int m = 1; m < abc.constants.getMultinameCount(); m++) { + int ni = abc.constants.getMultiname(m).name_index; + String n = ""; + if (ni > 0) { + n = abc.constants.getString(ni); + } + if (n.equals(oldName)) { + abc.renameMultiname(m, newName); + mulCount++; + } + } + } + + View.showMessageDialog(null, translate("rename.finished.multiname").replace("%count%", Integer.toString(mulCount))); + if (abcPanel != null) { + abcPanel.reload(); + } + + updateClassesList(); + reload(true); + ABCPanel abcPanel = getABCPanel(); + abcPanel.hilightScript(abcPanel.getSwf(), abcPanel.decompiledTextArea.getScriptLeaf().getClassPath().toString()); + } + } + } + + public List getASTreeNodes(TagTree tree) { + List result = new ArrayList<>(); + TagTreeModel tm = (TagTreeModel) tree.getModel(); + if (tm == null) { + return result; + } + TreeItem root = tm.getRoot(); + for (int i = 0; i < tm.getChildCount(root); i++) { + // first level node can be SWF and SWFBundle + TreeItem node = tm.getChild(root, i); + if (node instanceof SWFBundle) { + for (int j = 0; j < tm.getChildCount(node); j++) { + // child of SWFBundle should be SWF + SWF swfNode = (SWF) tm.getChild(node, j); + result.add(tm.getScriptsNode(swfNode)); + } + } else if (node instanceof SWF) { + SWF swfNode = (SWF) tm.getChild(root, i); + result.add(tm.getScriptsNode(swfNode)); + } + } + return result; + } + + public boolean confirmExperimental() { + return View.showConfirmDialog(null, translate("message.confirm.experimental"), translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION; + } + + public List exportSelection(AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException, InterruptedException { + + List ret = new ArrayList<>(); + List sel = folderPreviewPanel.selectedItems.isEmpty() ? tagTree.getAllSelected(tagTree) : new ArrayList<>(folderPreviewPanel.selectedItems.values()); + + List allSwfs = new ArrayList<>(); + for (SWFList swfList : swfs) { + for (SWF swf : swfList) { + allSwfs.add(swf); + } + } + + for (int j = 0; j < allSwfs.size(); j++) { + List as3scripts = new ArrayList<>(); + List images = new ArrayList<>(); + List shapes = new ArrayList<>(); + List morphshapes = new ArrayList<>(); + List buttons = new ArrayList<>(); + List movies = new ArrayList<>(); + List sounds = new ArrayList<>(); + List texts = new ArrayList<>(); + List as12scripts = new ArrayList<>(); + List binaryData = new ArrayList<>(); + Map> frames = new HashMap<>(); + List fonts = new ArrayList<>(); + List symbolNames = new ArrayList<>(); + + SWF swf = allSwfs.get(j); + for (TreeItem d : sel) { + SWF selectedNodeSwf = d.getSwf(); + if (!allSwfs.contains(selectedNodeSwf)) { + allSwfs.add(selectedNodeSwf); + } + + if (selectedNodeSwf != swf) { + continue; + } + + if (d instanceof TagScript) { + Tag tag = ((TagScript) d).getTag(); + if (tag instanceof DoActionTag || tag instanceof DoInitActionTag) { + as12scripts.add(d); + } + } + + if (d instanceof Tag || d instanceof ASMSource) { + TreeNodeType nodeType = TagTree.getTreeNodeType(d); + if (nodeType == TreeNodeType.IMAGE) { + images.add((Tag) d); + } + if (nodeType == TreeNodeType.SHAPE) { + shapes.add((Tag) d); + } + if (nodeType == TreeNodeType.BUTTON) { + buttons.add((Tag) d); + } + if (nodeType == TreeNodeType.MORPH_SHAPE) { + morphshapes.add((Tag) d); + } + if (nodeType == TreeNodeType.AS) { + as12scripts.add(d); + } + if (nodeType == TreeNodeType.MOVIE) { + movies.add((Tag) d); + } + if (nodeType == TreeNodeType.SOUND) { + sounds.add((Tag) d); + } + if (nodeType == TreeNodeType.BINARY_DATA) { + binaryData.add((Tag) d); + } + if (nodeType == TreeNodeType.TEXT) { + texts.add((Tag) d); + } + if (nodeType == TreeNodeType.FONT) { + fonts.add((Tag) d); + } + if (nodeType == TreeNodeType.OTHER_TAG) { + if (d instanceof SymbolClassTypeTag) { + symbolNames.add((Tag) d); + } + } + } + + if (d instanceof Frame) { + Frame fn = (Frame) d; + Timelined parent = fn.timeline.timelined; + int frame = fn.frame; + int parentId = 0; + if (parent instanceof CharacterTag) { + parentId = ((CharacterTag) parent).getCharacterId(); + } + if (!frames.containsKey(parentId)) { + frames.put(parentId, new ArrayList<>()); + } + frames.get(parentId).add(frame); + } + if (d instanceof ScriptPack) { + as3scripts.add((ScriptPack) d); + } + } + + EventListener evl = swf.getExportEventListener(); + + if (export.isOptionEnabled(ImageExportMode.class)) { + ret.addAll(new ImageExporter().exportImages(handler, selFile + File.separator + ImageExportSettings.EXPORT_FOLDER_NAME, images, + new ImageExportSettings(export.getValue(ImageExportMode.class)), evl)); + } + + if (export.isOptionEnabled(ShapeExportMode.class)) { + ret.addAll(new ShapeExporter().exportShapes(handler, selFile + File.separator + ShapeExportSettings.EXPORT_FOLDER_NAME, shapes, + new ShapeExportSettings(export.getValue(ShapeExportMode.class), export.getZoom()), evl)); + } + + if (export.isOptionEnabled(MorphShapeExportMode.class)) { + ret.addAll(new MorphShapeExporter().exportMorphShapes(handler, selFile + File.separator + MorphShapeExportSettings.EXPORT_FOLDER_NAME, morphshapes, + new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class), export.getZoom()), evl)); + } + + if (export.isOptionEnabled(TextExportMode.class)) { + ret.addAll(new TextExporter().exportTexts(handler, selFile + File.separator + TextExportSettings.EXPORT_FOLDER_NAME, texts, + new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get(), export.getZoom()), evl)); + } + + if (export.isOptionEnabled(MovieExportMode.class)) { + ret.addAll(new MovieExporter().exportMovies(handler, selFile + File.separator + MovieExportSettings.EXPORT_FOLDER_NAME, movies, + new MovieExportSettings(export.getValue(MovieExportMode.class)), evl)); + } + + if (export.isOptionEnabled(SoundExportMode.class)) { + ret.addAll(new SoundExporter().exportSounds(handler, selFile + File.separator + SoundExportSettings.EXPORT_FOLDER_NAME, sounds, + new SoundExportSettings(export.getValue(SoundExportMode.class)), evl)); + } + + if (export.isOptionEnabled(BinaryDataExportMode.class)) { + ret.addAll(new BinaryDataExporter().exportBinaryData(handler, selFile + File.separator + BinaryDataExportSettings.EXPORT_FOLDER_NAME, binaryData, + new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class)), evl)); + } + + if (export.isOptionEnabled(FontExportMode.class)) { + ret.addAll(new FontExporter().exportFonts(handler, selFile + File.separator + FontExportSettings.EXPORT_FOLDER_NAME, fonts, + new FontExportSettings(export.getValue(FontExportMode.class)), evl)); + } + + if (export.isOptionEnabled(SymbolClassExportMode.class)) { + ret.addAll(new SymbolClassExporter().exportNames(selFile, symbolNames, evl)); + } + + FrameExporter frameExporter = new FrameExporter(); + + if (export.isOptionEnabled(FrameExportMode.class)) { + FrameExportSettings fes = new FrameExportSettings(export.getValue(FrameExportMode.class), export.getZoom()); + for (Entry> entry : frames.entrySet()) { + int containerId = entry.getKey(); + if (containerId == 0) { + String subFolder = FrameExportSettings.EXPORT_FOLDER_NAME; + ret.addAll(frameExporter.exportFrames(handler, selFile + File.separator + subFolder, swf, containerId, entry.getValue(), fes, evl)); + } + } + } + + if (export.isOptionEnabled(SpriteExportMode.class)) { + SpriteExportSettings ses = new SpriteExportSettings(export.getValue(SpriteExportMode.class), export.getZoom()); + for (Entry> entry : frames.entrySet()) { + int containerId = entry.getKey(); + if (containerId != 0) { + String subFolder = SpriteExportSettings.EXPORT_FOLDER_NAME; + ret.addAll(frameExporter.exportFrames(handler, selFile + File.separator + subFolder, swf, containerId, entry.getValue(), ses, evl)); + } + } + } + + if (export.isOptionEnabled(ButtonExportMode.class)) { + ButtonExportSettings bes = new ButtonExportSettings(export.getValue(ButtonExportMode.class), export.getZoom()); + for (Tag tag : buttons) { + ButtonTag button = (ButtonTag) tag; + String subFolder = ButtonExportSettings.EXPORT_FOLDER_NAME; + List frameNums = new ArrayList<>(); + frameNums.add(0); // todo: export all frames + ret.addAll(frameExporter.exportFrames(handler, selFile + File.separator + subFolder, swf, button.getCharacterId(), frameNums, bes, evl)); + } + } + + if (export.isOptionEnabled(ScriptExportMode.class)) { + if (as3scripts.size() > 0 || as12scripts.size() > 0) { + boolean parallel = Configuration.parallelSpeedUp.get(); + String scriptsFolder = Path.combine(selFile, ScriptExportSettings.EXPORT_FOLDER_NAME); + Path.createDirectorySafe(new File(scriptsFolder)); + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), !parallel && Configuration.scriptExportSingleFile.get()); + String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); + try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { + scriptExportSettings.singleFileWriter = writer; + if (swf.isAS3()) { + ret.addAll(new AS3ScriptExporter().exportActionScript3(swf, handler, scriptsFolder, as3scripts, scriptExportSettings, parallel, evl)); + } else { + Map asmsToExport = swf.getASMs(true, as12scripts, false); + ret.addAll(new AS2ScriptExporter().exportAS2Scripts(handler, scriptsFolder, asmsToExport, scriptExportSettings, parallel, evl)); + } + } + } + } + } + + return ret; + } + + public void exportAll(SWF swf, AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException, InterruptedException { + boolean exportAll = false; + if (exportAll) { + exportAllDebug(swf, handler, selFile, export); + return; + } + + EventListener evl = swf.getExportEventListener(); + + if (export.isOptionEnabled(ImageExportMode.class)) { + new ImageExporter().exportImages(handler, Path.combine(selFile, ImageExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new ImageExportSettings(export.getValue(ImageExportMode.class)), evl); + } + + if (export.isOptionEnabled(ShapeExportMode.class)) { + new ShapeExporter().exportShapes(handler, Path.combine(selFile, ShapeExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new ShapeExportSettings(export.getValue(ShapeExportMode.class), export.getZoom()), evl); + } + + if (export.isOptionEnabled(MorphShapeExportMode.class)) { + new MorphShapeExporter().exportMorphShapes(handler, Path.combine(selFile, MorphShapeExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class), export.getZoom()), evl); + } + + if (export.isOptionEnabled(TextExportMode.class)) { + new TextExporter().exportTexts(handler, Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get(), export.getZoom()), evl); + } + + if (export.isOptionEnabled(MovieExportMode.class)) { + new MovieExporter().exportMovies(handler, Path.combine(selFile, MovieExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new MovieExportSettings(export.getValue(MovieExportMode.class)), evl); + } + + if (export.isOptionEnabled(SoundExportMode.class)) { + new SoundExporter().exportSounds(handler, Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new SoundExportSettings(export.getValue(SoundExportMode.class)), evl); + } + + if (export.isOptionEnabled(BinaryDataExportMode.class)) { + new BinaryDataExporter().exportBinaryData(handler, Path.combine(selFile, BinaryDataExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class)), evl); + } + + if (export.isOptionEnabled(FontExportMode.class)) { + new FontExporter().exportFonts(handler, Path.combine(selFile, FontExportSettings.EXPORT_FOLDER_NAME), swf.tags, + new FontExportSettings(export.getValue(FontExportMode.class)), evl); + } + + if (export.isOptionEnabled(SymbolClassExportMode.class)) { + new SymbolClassExporter().exportNames(selFile, swf.tags, evl); + } + + FrameExporter frameExporter = new FrameExporter(); + + if (export.isOptionEnabled(FrameExportMode.class)) { + FrameExportSettings fes = new FrameExportSettings(export.getValue(FrameExportMode.class), export.getZoom()); + frameExporter.exportFrames(handler, Path.combine(selFile, FrameExportSettings.EXPORT_FOLDER_NAME), swf, 0, null, fes, evl); + } + + if (export.isOptionEnabled(SpriteExportMode.class)) { + SpriteExportSettings ses = new SpriteExportSettings(export.getValue(SpriteExportMode.class), export.getZoom()); + for (CharacterTag c : swf.getCharacters().values()) { + if (c instanceof DefineSpriteTag) { + frameExporter.exportFrames(handler, Path.combine(selFile, SpriteExportSettings.EXPORT_FOLDER_NAME), swf, c.getCharacterId(), null, ses, evl); + } + } + } + + if (export.isOptionEnabled(ButtonExportMode.class)) { + ButtonExportSettings bes = new ButtonExportSettings(export.getValue(ButtonExportMode.class), export.getZoom()); + for (CharacterTag c : swf.getCharacters().values()) { + if (c instanceof ButtonTag) { + List frameNums = new ArrayList<>(); + frameNums.add(0); // todo: export all frames + frameExporter.exportFrames(handler, Path.combine(selFile, ButtonExportSettings.EXPORT_FOLDER_NAME), swf, c.getCharacterId(), frameNums, bes, evl); + } + } + } + + if (export.isOptionEnabled(ScriptExportMode.class)) { + boolean parallel = Configuration.parallelSpeedUp.get(); + String scriptsFolder = Path.combine(selFile, ScriptExportSettings.EXPORT_FOLDER_NAME); + Path.createDirectorySafe(new File(scriptsFolder)); + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), !parallel && Configuration.scriptExportSingleFile.get()); + String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); + try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { + scriptExportSettings.singleFileWriter = writer; + swf.exportActionScript(handler, scriptsFolder, scriptExportSettings, parallel, evl); + } + } + } + + public void exportAllDebug(SWF swf, AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException, InterruptedException { + EventListener evl = swf.getExportEventListener(); + + if (export.isOptionEnabled(ImageExportMode.class)) { + for (ImageExportMode exportMode : ImageExportMode.values()) { + new ImageExporter().exportImages(handler, Path.combine(selFile, ImageExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new ImageExportSettings(exportMode), evl); + } + } + + if (export.isOptionEnabled(ShapeExportMode.class)) { + for (ShapeExportMode exportMode : ShapeExportMode.values()) { + new ShapeExporter().exportShapes(handler, Path.combine(selFile, ShapeExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new ShapeExportSettings(exportMode, export.getZoom()), evl); + } + } + + if (export.isOptionEnabled(MorphShapeExportMode.class)) { + for (MorphShapeExportMode exportMode : MorphShapeExportMode.values()) { + new MorphShapeExporter().exportMorphShapes(handler, Path.combine(selFile, MorphShapeExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new MorphShapeExportSettings(exportMode, export.getZoom()), evl); + } + } + + if (export.isOptionEnabled(TextExportMode.class)) { + for (TextExportMode exportMode : TextExportMode.values()) { + new TextExporter().exportTexts(handler, Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new TextExportSettings(exportMode, Configuration.textExportSingleFile.get(), export.getZoom()), evl); + } + } + + if (export.isOptionEnabled(MovieExportMode.class)) { + for (MovieExportMode exportMode : MovieExportMode.values()) { + new MovieExporter().exportMovies(handler, Path.combine(selFile, MovieExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new MovieExportSettings(exportMode), evl); + } + } + + if (export.isOptionEnabled(SoundExportMode.class)) { + for (SoundExportMode exportMode : SoundExportMode.values()) { + new SoundExporter().exportSounds(handler, Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new SoundExportSettings(exportMode), evl); + } + } + + if (export.isOptionEnabled(BinaryDataExportMode.class)) { + for (BinaryDataExportMode exportMode : BinaryDataExportMode.values()) { + new BinaryDataExporter().exportBinaryData(handler, Path.combine(selFile, BinaryDataExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new BinaryDataExportSettings(exportMode), evl); + } + } + + if (export.isOptionEnabled(FontExportMode.class)) { + for (FontExportMode exportMode : FontExportMode.values()) { + new FontExporter().exportFonts(handler, Path.combine(selFile, FontExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.tags, + new FontExportSettings(exportMode), evl); + } + } + + if (export.isOptionEnabled(SymbolClassExportMode.class)) { + for (SymbolClassExportMode exportMode : SymbolClassExportMode.values()) { + new SymbolClassExporter().exportNames(selFile, swf.tags, evl); + } + } + + FrameExporter frameExporter = new FrameExporter(); + + if (export.isOptionEnabled(FrameExportMode.class)) { + for (FrameExportMode exportMode : FrameExportMode.values()) { + FrameExportSettings fes = new FrameExportSettings(exportMode, export.getZoom()); + frameExporter.exportFrames(handler, Path.combine(selFile, FrameExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, 0, null, fes, evl); + } + } + + if (export.isOptionEnabled(SpriteExportMode.class)) { + for (SpriteExportMode exportMode : SpriteExportMode.values()) { + SpriteExportSettings ses = new SpriteExportSettings(exportMode, export.getZoom()); + for (CharacterTag c : swf.getCharacters().values()) { + if (c instanceof DefineSpriteTag) { + frameExporter.exportFrames(handler, Path.combine(selFile, SpriteExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, c.getCharacterId(), null, ses, evl); + } + } + } + } + + if (export.isOptionEnabled(ButtonExportMode.class)) { + for (ButtonExportMode exportMode : ButtonExportMode.values()) { + ButtonExportSettings bes = new ButtonExportSettings(exportMode, export.getZoom()); + for (CharacterTag c : swf.getCharacters().values()) { + if (c instanceof ButtonTag) { + List frameNums = new ArrayList<>(); + frameNums.add(0); // todo: export all frames + frameExporter.exportFrames(handler, Path.combine(selFile, ButtonExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, c.getCharacterId(), frameNums, bes, evl); + } + } + } + } + + if (export.isOptionEnabled(ScriptExportMode.class)) { + boolean parallel = Configuration.parallelSpeedUp.get(); + for (ScriptExportMode exportMode : ScriptExportMode.values()) { + String scriptsFolder = Path.combine(selFile, ScriptExportSettings.EXPORT_FOLDER_NAME, exportMode.name()); + Path.createDirectorySafe(new File(scriptsFolder)); + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(exportMode, !parallel && Configuration.scriptExportSingleFile.get()); + String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); + try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { + scriptExportSettings.singleFileWriter = writer; + swf.exportActionScript(handler, scriptsFolder, scriptExportSettings, parallel, evl); + } + } + } + } + + public List getSwfs() { + return swfs; + } + + public SWFList getCurrentSwfList() { + SWF swf = getCurrentSwf(); + if (swf == null) { + return null; + } + + return swf.swfList; + } + + public SWF getCurrentSwf() { + if (swfs == null || swfs.isEmpty()) { + return null; + } + + if (treePanelMode == TreePanelMode.TAG_TREE) { + TreeItem treeNode = (TreeItem) tagTree.getLastSelectedPathComponent(); + if (treeNode == null) { + return swfs.get(0).get(0); + } + + if (treeNode instanceof SWFList) { + return null; + } + + return treeNode.getSwf(); + } else if (treePanelMode == TreePanelMode.DUMP_TREE) { + DumpInfo dumpInfo = (DumpInfo) dumpTree.getLastSelectedPathComponent(); + + if (dumpInfo == null) { + return null; + } + + return DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); + } + + return null; + } + + public void gotoFrame(int frame) { + TreeItem treeItem = (TreeItem) tagTree.getLastSelectedPathComponent(); + if (treeItem == null) { + return; + } + if (treeItem instanceof Timelined) { + Timelined t = (Timelined) treeItem; + Frame f = tagTree.getModel().getFrame(treeItem.getSwf(), t, frame); + if (f != null) { + setTagTreeSelectedNode(f); + } + } + } + + public void gotoDocumentClass(SWF swf) { + if (swf == null) { + return; + } + + String documentClass = swf.getDocumentClass(); + if (documentClass != null && !Configuration.dumpView.get()) { + List abcList = swf.getAbcList(); + if (!abcList.isEmpty()) { + ABCPanel abcPanel = getABCPanel(); + abcPanel.setAbc(abcList.get(0).getABC()); + abcPanel.hilightScript(swf, documentClass); + } + } + } + + public void disableDecompilationChanged() { + clearAllScriptCache(); + + if (abcPanel != null) { + abcPanel.reload(); + } + + updateClassesList(); + } + + private void clearAllScriptCache() { + for (SWFList swfList : swfs) { + for (SWF swf : swfList) { + swf.clearScriptCache(); + } + } + } + + public void searchInActionScriptOrText(Boolean searchInText) { + SearchDialog searchDialog = new SearchDialog(getMainFrame().getWindow(), false); + if (searchInText != null) { + if (searchInText) { + searchDialog.searchInTextsRadioButton.setSelected(true); + } else { + searchDialog.searchInASRadioButton.setSelected(true); + } + } + + if (searchDialog.showDialog() == AppDialog.OK_OPTION) { + final String txt = searchDialog.searchField.getText(); + if (!txt.isEmpty()) { + final SWF swf = getCurrentSwf(); + if (swf.isAS3()) { + getABCPanel(); + } else { + getActionPanel(); + } + + boolean ignoreCase = searchDialog.ignoreCaseCheckBox.isSelected(); + boolean regexp = searchDialog.regexpCheckBox.isSelected(); + + if (searchDialog.searchInASRadioButton.isSelected()) { + new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + List abcResult = null; + List actionResult = null; + if (swf.isAS3()) { + abcResult = getABCPanel().search(txt, ignoreCase, regexp, this); + } else { + actionResult = getActionPanel().search(txt, ignoreCase, regexp, this); + } + + List fAbcResult = abcResult; + List fActionResult = actionResult; + View.execInEventDispatch(() -> { + boolean found = false; + if (fAbcResult != null) { + found = true; + getABCPanel().searchPanel.setSearchText(txt); + SearchResultsDialog sr = new SearchResultsDialog<>(getMainFrame().getWindow(), txt, getABCPanel()); + sr.setResults(fAbcResult); + sr.setVisible(true); + } else if (fActionResult != null) { + found = true; + getActionPanel().searchPanel.setSearchText(txt); + + SearchResultsDialog sr = new SearchResultsDialog<>(getMainFrame().getWindow(), txt, getActionPanel()); + sr.setResults(fActionResult); + sr.setVisible(true); + } + + if (!found) { + View.showMessageDialog(null, translate("message.search.notfound").replace("%searchtext%", txt), translate("message.search.notfound.title"), JOptionPane.INFORMATION_MESSAGE); + } + + Main.stopWork(); + }); + + return null; + } + + @Override + protected void done() { + View.execInEventDispatch(() -> { + Main.stopWork(); + }); + + } + }.execute(); + } else if (searchDialog.searchInTextsRadioButton.isSelected()) { + new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + List textResult = null; + SearchPanel textSearchPanel = previewPanel.getTextPanel().getSearchPanel(); + textSearchPanel.setOptions(ignoreCase, regexp); + textResult = searchText(txt, ignoreCase, regexp, swf); + + List fTextResult = textResult; + View.execInEventDispatch(() -> { + textSearchPanel.setSearchText(txt); + boolean found = textSearchPanel.setResults(fTextResult); + if (!found) { + View.showMessageDialog(null, translate("message.search.notfound").replace("%searchtext%", txt), translate("message.search.notfound.title"), JOptionPane.INFORMATION_MESSAGE); + } + + Main.stopWork(); + }); + + return null; + } + + @Override + protected void done() { + View.execInEventDispatch(() -> { + Main.stopWork(); + }); + + } + }.execute(); + } + } + } + } + + public void replaceText() { + SearchDialog replaceDialog = new SearchDialog(getMainFrame().getWindow(), true); + if (replaceDialog.showDialog() == AppDialog.OK_OPTION) { + final String txt = replaceDialog.searchField.getText(); + if (!txt.isEmpty()) { + final SWF swf = getCurrentSwf(); + + new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + int findCount = 0; + boolean ignoreCase = replaceDialog.ignoreCaseCheckBox.isSelected(); + boolean regexp = replaceDialog.regexpCheckBox.isSelected(); + String replacement = replaceDialog.replaceField.getText(); + if (!regexp) { + replacement = Matcher.quoteReplacement(replacement); + } + Pattern pat; + if (regexp) { + pat = Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); + } else { + pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); + } + List textTags = new ArrayList<>(); + for (Tag tag : swf.tags) { + if (tag instanceof TextTag) { + textTags.add((TextTag) tag); + } + } + for (TextTag textTag : textTags) { + if (!replaceDialog.replaceInParametersCheckBox.isSelected()) { + List texts = textTag.getTexts(); + boolean found = false; + for (int i = 0; i < texts.size(); i++) { + String text = texts.get(i); + if (pat.matcher(text).find()) { + texts.set(i, text.replaceAll(txt, replacement)); + found = true; + findCount++; + } + } + if (found) { + String[] textArray = texts.toArray(new String[texts.size()]); + textTag.setFormattedText(getMissingCharacterHandler(), textTag.getFormattedText().text, textArray); + } + } else { + String text = textTag.getFormattedText().text; + if (pat.matcher(text).find()) { + textTag.setFormattedText(getMissingCharacterHandler(), text.replaceAll(txt, replacement), null); + findCount++; + } + } + } + + if (findCount > 0) { + swf.clearImageCache(); + repaintTree(); + } + + return null; + } + }.execute(); + } + } + } + + private List searchText(String txt, boolean ignoreCase, boolean regexp, SWF swf) { + if (txt != null && !txt.isEmpty()) { + List found = new ArrayList<>(); + Pattern pat; + if (regexp) { + pat = Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); + } else { + pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); + } + for (Tag tag : swf.tags) { + if (tag instanceof TextTag) { + TextTag textTag = (TextTag) tag; + if (pat.matcher(textTag.getFormattedText().text).find()) { + found.add(textTag); + } + } + } + + return found; + } + + return null; + } + + @Override + public void updateSearchPos(TextTag item) { + setTagTreeSelectedNode(item); + previewPanel.getTextPanel().updateSearchPos(); + } + + private void setDumpTreeSelectedNode(DumpInfo dumpInfo) { + DumpTreeModel dtm = (DumpTreeModel) dumpTree.getModel(); + TreePath tp = dtm.getDumpInfoPath(dumpInfo); + if (tp != null) { + dumpTree.setSelectionPath(tp); + dumpTree.scrollPathToVisible(tp); + } else { + showCard(CARDEMPTYPANEL); + } + } + + public void setTagTreeSelectedNode(TreeItem treeItem) { + TagTreeModel ttm = tagTree.getModel(); + TreePath tp = ttm.getTreePath(treeItem); + if (tp != null) { + tagTree.setSelectionPath(tp); + tagTree.scrollPathToVisible(tp); + } else { + showCard(CARDEMPTYPANEL); + } + } + + public void autoDeobfuscateChanged() { + Helper.decompilationErrorAdd = AppStrings.translate(Configuration.autoDeobfuscate.get() ? "deobfuscation.comment.failed" : "deobfuscation.comment.tryenable"); + clearAllScriptCache(); + if (abcPanel != null) { + abcPanel.reload(); + } + reload(true); + updateClassesList(); + } + + public void renameOneIdentifier(final SWF swf) { + if (swf == null) { + return; + } + + FileAttributesTag fileAttributes = swf.getFileAttributes(); + if (fileAttributes != null && fileAttributes.actionScript3) { + final int multiName = getABCPanel().decompiledTextArea.getMultinameUnderCaret(); + final List abcList = swf.getAbcList(); + if (multiName > 0) { + new CancellableWorker() { + @Override + public Void doInBackground() throws Exception { + renameMultiname(abcList, multiName); + return null; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.renaming") + "...", this); + } + + @Override + protected void done() { + Main.stopWork(); + } + }.execute(); + + } else { + View.showMessageDialog(null, translate("message.rename.notfound.multiname"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE); + } + } else { + final String identifier = getActionPanel().getStringUnderCursor(); + if (identifier != null) { + new CancellableWorker() { + @Override + public Void doInBackground() throws Exception { + try { + renameIdentifier(swf, identifier); + } catch (InterruptedException ex) { + logger.log(Level.SEVERE, null, ex); + } + return null; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.renaming") + "...", this); + } + + @Override + protected void done() { + Main.stopWork(); + } + }.execute(); + } else { + View.showMessageDialog(null, translate("message.rename.notfound.identifier"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE); + } + } + } + + public void exportFla(final SWF swf) { + if (swf == null) { + return; + } + JFileChooser fc = new JFileChooser(); + String selDir = Configuration.lastOpenDir.get(); + fc.setCurrentDirectory(new File(selDir)); + if (!selDir.endsWith(File.separator)) { + selDir += File.separator; + } + String fileName = new File(swf.getFile()).getName(); + fileName = fileName.substring(0, fileName.length() - 4) + ".fla"; + fc.setSelectedFile(new File(selDir + fileName)); + List flaFilters = new ArrayList<>(); + List xflFilters = new ArrayList<>(); + List versions = new ArrayList<>(); + boolean isAS3 = swf.isAS3(); + for (int i = FLAVersion.values().length - 1; i >= 0; i--) { + final FLAVersion v = FLAVersion.values()[i]; + if (!isAS3 && v.minASVersion() > 2) { + // This version does not support AS1/2 + } else { + versions.add(v); + FileFilter f = new FileFilter() { + @Override + public boolean accept(File f) { + return f.isDirectory() || (f.getName().toLowerCase().endsWith(".fla")); + } + + @Override + public String getDescription() { + return translate("filter.fla").replace("%version%", v.applicationName()); + } + }; + if (v == FLAVersion.CS6) { + fc.setFileFilter(f); + } else { + fc.addChoosableFileFilter(f); + } + flaFilters.add(f); + f = new FileFilter() { + @Override + public boolean accept(File f) { + return f.isDirectory() || (f.getName().toLowerCase().endsWith(".xfl")); + } + + @Override + public String getDescription() { + return translate("filter.xfl").replace("%version%", v.applicationName()); + } + }; + fc.addChoosableFileFilter(f); + xflFilters.add(f); + } + } + + fc.setAcceptAllFileFilterUsed(false); + JFrame f = new JFrame(); + View.setWindowIcon(f); + if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) { + Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); + File sf = Helper.fixDialogFile(fc.getSelectedFile()); + + FileFilter selectedFilter = fc.getFileFilter(); + final boolean compressed = flaFilters.contains(selectedFilter); + String path = sf.getAbsolutePath(); + if (path.endsWith(".fla") || path.endsWith(".xfl")) { + path = path.substring(0, path.length() - 4); + } + path += compressed ? ".fla" : ".xfl"; + final FLAVersion selectedVersion = versions.get(compressed ? flaFilters.indexOf(selectedFilter) : xflFilters.indexOf(selectedFilter)); + final File selfile = new File(path); + new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + Helper.freeMem(); + try { + AbortRetryIgnoreHandler errorHandler = new GuiAbortRetryIgnoreHandler(); + if (compressed) { + swf.exportFla(errorHandler, selfile.getAbsolutePath(), new File(swf.getFile()).getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), selectedVersion); + } else { + swf.exportXfl(errorHandler, selfile.getAbsolutePath(), new File(swf.getFile()).getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), selectedVersion); + } + } catch (Exception ex) { + View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage(), translate("error"), JOptionPane.ERROR_MESSAGE); + } + Helper.freeMem(); + return null; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.exporting.fla") + "...", this); + } + + @Override + protected void done() { + if (Configuration.openFolderAfterFlaExport.get()) { + try { + Desktop.getDesktop().open(selfile.getAbsoluteFile().getParentFile()); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + Main.stopWork(); + } + }.execute(); + } + } + + public void importText(final SWF swf) { + JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); + chooser.setDialogTitle(translate("import.select.directory")); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setAcceptAllFileFilterUsed(false); + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); + File textsFile = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, TextExporter.TEXT_EXPORT_FILENAME_FORMATTED)); + TextImporter textImporter = new TextImporter(getMissingCharacterHandler(), new TextImportErrorHandler() { + + // "configuration items" for the current replace only + private final ConfigurationItem showAgainImportError = new ConfigurationItem<>("showAgainImportError", true, true); + + private final ConfigurationItem showAgainInvalidText = new ConfigurationItem<>("showAgainInvalidText", true, true); + + private String getTextTagInfo(TextTag textTag) { + StringBuilder ret = new StringBuilder(); + if (textTag != null) { + ret.append(" TextId: ").append(textTag.getCharacterId()).append(" (").append(String.join(", ", textTag.getTexts())).append(")"); + } + + return ret.toString(); + } + + @Override + public boolean handle(TextTag textTag) { + String msg = translate("error.text.import"); + logger.log(Level.SEVERE, msg + getTextTagInfo(textTag)); + return View.showConfirmDialog(MainPanel.this, msg, translate("error"), JOptionPane.OK_CANCEL_OPTION, showAgainImportError, JOptionPane.OK_OPTION) != JOptionPane.OK_OPTION; + } + + @Override + public boolean handle(TextTag textTag, String message, long line) { + String msg = translate("error.text.invalid.continue").replace("%text%", message).replace("%line%", Long.toString(line)); + logger.log(Level.SEVERE, msg + getTextTagInfo(textTag)); + return View.showConfirmDialog(MainPanel.this, msg, translate("error"), JOptionPane.OK_CANCEL_OPTION, showAgainInvalidText, JOptionPane.OK_OPTION) != JOptionPane.OK_OPTION; + } + }); + + // try to import formatted texts + if (textsFile.exists()) { + textImporter.importTextsSingleFileFormatted(textsFile, swf); + } else { + textsFile = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, TextExporter.TEXT_EXPORT_FILENAME_PLAIN)); + // try to import plain texts + if (textsFile.exists()) { + textImporter.importTextsSingleFile(textsFile, swf); + } else { + textImporter.importTextsMultipleFiles(selFile, swf); + } + } + + swf.clearImageCache(); + reload(true); + } + } + + public void importScript(final SWF swf) { + JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); + chooser.setDialogTitle(translate("import.select.directory")); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setAcceptAllFileFilterUsed(false); + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); + String scriptsFolder = Path.combine(selFile, ScriptExportSettings.EXPORT_FOLDER_NAME); + + int countAs2 = new AS2ScriptImporter().importScripts(scriptsFolder, swf.getASMs(true)); + int countAs3 = new AS3ScriptImporter().importScripts(scriptsFolder, swf.getAS3Packs()); + + if (countAs3 > 0) { + updateClassesList(); + } + + if (countAs2 == 0 && countAs3 == 0 && swf.isAS3()) { + View.showMessageDialog(this, translate("import.script.as12warning")); + } else { + View.showMessageDialog(this, translate("import.script.result").replace("%count%", Integer.toString(countAs2))); + reload(true); + } + } + } + + public void importSymbolClass(final SWF swf) { + JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); + chooser.setDialogTitle(translate("import.select.directory")); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setAcceptAllFileFilterUsed(false); + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); + File importFile = new File(Path.combine(selFile, SymbolClassExporter.SYMBOL_CLASS_EXPORT_FILENAME)); + SymbolClassImporter importer = new SymbolClassImporter(); + + if (importFile.exists()) { + importer.importSymbolClasses(importFile, swf); + } + } + } + + private String selectExportDir() { + JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); + chooser.setDialogTitle(translate("export.select.directory")); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setAcceptAllFileFilterUsed(false); + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + final String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); + Configuration.lastExportDir.set(Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath()); + return selFile; + } + return null; + } + + public void export(final boolean onlySel) { + + final SWF swf = getCurrentSwf(); + List sel = tagTree.getSelection(swf); + if (!onlySel) { + sel = null; + } else { + if (sel.isEmpty()) { + return; + } + } + final ExportDialog export = new ExportDialog(sel); + if (export.showExportDialog() == AppDialog.OK_OPTION) { + final String selFile = selectExportDir(); + if (selFile != null) { + final long timeBefore = System.currentTimeMillis(); + + new CancellableWorker() { + @Override + public Void doInBackground() throws Exception { + try { + AbortRetryIgnoreHandler errorHandler = new GuiAbortRetryIgnoreHandler(); + if (onlySel) { + exportSelection(errorHandler, selFile, export); + } else { + exportAll(swf, errorHandler, selFile, export); + } + } catch (Exception ex) { + logger.log(Level.SEVERE, "Error during export", ex); + View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage()); + } + return null; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.exporting") + "...", this); + } + + @Override + protected void done() { + Main.stopWork(); + long timeAfter = System.currentTimeMillis(); + final long timeMs = timeAfter - timeBefore; + + View.execInEventDispatchLater(() -> { + setStatus(translate("export.finishedin").replace("%time%", Helper.formatTimeSec(timeMs))); + }); + } + }.execute(); + + } + } + } + + public void exportJavaSource() { + List sel = tagTree.getSelected(tagTree); + for (TreeItem item : sel) { + if (item instanceof SWF) { + SWF swf = (SWF) item; + final String selFile = selectExportDir(); + if (selFile != null) { + Main.startWork(translate("work.exporting") + "...", null); + + try { + new SwfJavaExporter().exportJavaCode(swf, selFile); + Main.stopWork(); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + } + } + + public void exportSwfXml() { + List sel = tagTree.getSelected(tagTree); + Set swfs = new HashSet<>(); + + for (TreeItem item : sel) { + swfs.add(item.getSwf()); + } + + for (SWF swf : swfs) { + final String selFile = selectExportDir(); + if (selFile != null) { + Main.startWork(translate("work.exporting") + "...", null); + + try { + File outFile = new File(selFile + File.separator + Helper.makeFileName("swf.xml")); + new SwfXmlExporter().exportXml(swf, outFile); + Main.stopWork(); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + } + + public void importSwfXml() { + List sel = tagTree.getSelected(tagTree); + Set swfs = new HashSet<>(); + for (TreeItem item : sel) { + swfs.add(item.getSwf()); + } + if (swfs.size() > 1) { + return; + } + + for (SWF swf : swfs) { + File selectedFile = showImportFileChooser("filter.xml|*.xml"); + if (selectedFile != null) { + File selfile = Helper.fixDialogFile(selectedFile); + String xml = Helper.readTextFile(selfile.getPath()); + try { + new SwfXmlImporter().importSwf(swf, xml); + swf.clearAllCache(); + swf.assignExportNamesToSymbols(); + swf.assignClassesToSymbols(); + refreshTree(swf); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + } + + public void restoreControlFlow(final boolean all) { + if ((!all) || confirmExperimental()) { + new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + ABCPanel abcPanel = getABCPanel(); + if (all) { + for (ABCContainerTag tag : abcPanel.getAbcList()) { + tag.getABC().restoreControlFlow(); + } + } else { + ABC abc = abcPanel.abc; + int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); + if (bi != -1) { + abc.bodies.get(bi).restoreControlFlow(abc.constants, abcPanel.decompiledTextArea.getCurrentTrait(), abc.method_info.get(abc.bodies.get(bi).method_info)); + } + + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abc, abcPanel.decompiledTextArea.getCurrentTrait(), abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getScriptIndex()); + } + return null; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.restoringControlFlow"), this); + } + + @Override + protected void done() { + View.execInEventDispatch(() -> { + Main.stopWork(); + View.showMessageDialog(null, translate("work.restoringControlFlow.complete")); + + getABCPanel().reload(); + updateClassesList(); + }); + } + }.execute(); + } + } + + public void renameIdentifiers(final SWF swf) { + if (swf == null) { + return; + } + if (confirmExperimental()) { + RenameDialog renameDialog = new RenameDialog(); + if (renameDialog.showRenameDialog() == AppDialog.OK_OPTION) { + final RenameType renameType = renameDialog.getRenameType(); + new CancellableWorker() { + @Override + protected Integer doInBackground() throws Exception { + int cnt = swf.deobfuscateIdentifiers(renameType); + return cnt; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.renaming.identifiers") + "...", this); + } + + @Override + protected void done() { + View.execInEventDispatch(() -> { + try { + int cnt = get(); + Main.stopWork(); + View.showMessageDialog(null, translate("message.rename.renamed").replace("%count%", Integer.toString(cnt))); + swf.assignClassesToSymbols(); + swf.clearScriptCache(); + if (abcPanel != null) { + abcPanel.reload(); + } + updateClassesList(); + reload(true); + } catch (Exception ex) { + logger.log(Level.SEVERE, "Error during renaming identifiers", ex); + Main.stopWork(); + View.showMessageDialog(null, translate("error.occured").replace("%error%", ex.getClass().getSimpleName())); + } + }); + } + }.execute(); + } + } + } + + public void deobfuscate() { + DeobfuscationDialog deobfuscationDialog = new DeobfuscationDialog(); + if (deobfuscationDialog.showDialog() == AppDialog.OK_OPTION) { + DeobfuscationLevel level = DeobfuscationLevel.getByLevel(deobfuscationDialog.codeProcessingLevel.getValue()); + new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + try { + ABCPanel abcPanel = getABCPanel(); + if (deobfuscationDialog.processAllCheckbox.isSelected()) { + SWF swf = abcPanel.getSwf(); + swf.deobfuscate(level); + } else { + int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); + DecompiledEditorPane decompiledTextArea = abcPanel.decompiledTextArea; + Trait t = abcPanel.decompiledTextArea.getCurrentTrait(); + ABC abc = abcPanel.abc; + if (bi != -1) { + int scriptIndex = decompiledTextArea.getScriptLeaf().scriptIndex; + int classIndex = decompiledTextArea.getClassIndex(); + boolean isStatic = decompiledTextArea.getIsStatic(); + abc.bodies.get(bi).deobfuscate(level, t, scriptIndex, classIndex, isStatic, ""/*FIXME*/); + } + + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abc, t, abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getScriptIndex()); + } + } catch (Exception ex) { + logger.log(Level.SEVERE, "Deobfuscation error", ex); + } + + return null; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.deobfuscating") + "...", this); + } + + @Override + protected void done() { + View.execInEventDispatch(() -> { + Main.stopWork(); + View.showMessageDialog(null, translate("work.deobfuscating.complete")); + + clearAllScriptCache(); + getABCPanel().reload(); + updateClassesList(); + }); + } + }.execute(); + } + } + + public void removeNonScripts(SWF swf) { + if (swf == null) { + return; + } + + List tags = new ArrayList<>(swf.tags); + List toRemove = new ArrayList<>(); + for (Tag tag : tags) { + System.out.println(tag.getClass()); + if (!(tag instanceof ABCContainerTag || tag instanceof ASMSource)) { + toRemove.add(tag); + } + } + + swf.removeTags(toRemove, true); + refreshTree(swf); + } + + private void clear() { + dumpViewPanel.clear(); + previewPanel.clear(); + headerPanel.clear(); + folderPreviewPanel.clear(); + if (abcPanel != null) { + abcPanel.clearSwf(); + } + if (actionPanel != null) { + actionPanel.clearSource(); + } + } + + public void refreshTree() { + refreshTree(new SWF[0]); + } + + public void refreshTree(SWF swf) { + refreshTree(new SWF[]{swf}); + } + + public void refreshTree(SWF[] swfs) { + clear(); + showCard(CARDEMPTYPANEL); + TreeItem treeItem = tagTree.getCurrentTreeItem(); + + tagTree.updateSwfs(swfs); + + if (treeItem != null) { + SWF treeItemSwf = treeItem.getSwf().getRootSwf(); + if (this.swfs.contains(treeItemSwf.swfList)) { + setTagTreeSelectedNode(treeItem); + } + } + + reload(true); + } + + public void refreshDecompiled() { + clearAllScriptCache(); + if (abcPanel != null) { + abcPanel.reload(); + } + + reload(true); + updateClassesList(); + } + + private MissingCharacterHandler getMissingCharacterHandler() { + return new MissingCharacterHandler() { + + // "configuration items" for the current replace only + private final ConfigurationItem showAgainIgnoreMissingCharacters = new ConfigurationItem<>("showAgainIgnoreMissingCharacters", true, true); + + private boolean ignoreMissingCharacters = false; + + @Override + public boolean getIgnoreMissingCharacters() { + return ignoreMissingCharacters; + } + + @Override + public boolean handle(TextTag textTag, final FontTag font, final char character) { + String fontName = font.getSwf().sourceFontNamesMap.get(font.getFontId()); + if (fontName == null) { + fontName = font.getFontName(); + } + final Font f = FontTag.installedFontsByName.get(fontName); + if (f == null || !f.canDisplay(character)) { + String msg = translate("error.font.nocharacter").replace("%char%", "" + character); + logger.log(Level.SEVERE, msg + " FontId: " + font.getCharacterId() + " TextId: " + textTag.getCharacterId()); + ignoreMissingCharacters = View.showConfirmDialog(null, msg, translate("error"), + JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE, + showAgainIgnoreMissingCharacters, + ignoreMissingCharacters ? JOptionPane.OK_OPTION : JOptionPane.CANCEL_OPTION) == JOptionPane.OK_OPTION; + return false; + } + + font.addCharacter(character, f); + + return true; + } + }; + } + + public boolean saveText(TextTag textTag, String formattedText, String[] texts) { + try { + if (textTag.setFormattedText(getMissingCharacterHandler(), formattedText, texts)) { + return true; + } + } catch (TextParseException ex) { + View.showMessageDialog(null, translate("error.text.invalid").replace("%text%", ex.text).replace("%line%", Long.toString(ex.line)), translate("error"), JOptionPane.ERROR_MESSAGE); + } + + return false; + } + + public boolean alignText(TextTag textTag, TextAlign textAlign) { + if (textTag.alignText(textAlign)) { + return true; + } + + return false; + } + + public boolean translateText(TextTag textTag, int diff) { + if (textTag.translateText(diff)) { + return true; + } + + return false; + } + + public boolean previousTag() { + if (getCurrentView() == VIEW_RESOURCES) { + if (tagTree.getSelectionRows().length > 0) { + int row = tagTree.getSelectionRows()[0]; + if (row > 0) { + tagTree.setSelectionRow(row - 1); + tagTree.scrollRowToVisible(row - 1); + previewPanel.focusTextPanel(); + } + } + return true; + } + return false; + } + + public boolean nextTag() { + if (getCurrentView() == VIEW_RESOURCES) { + if (tagTree.getSelectionRows().length > 0) { + int row = tagTree.getSelectionRows()[0]; + if (row < tagTree.getRowCount() - 1) { + tagTree.setSelectionRow(row + 1); + tagTree.scrollRowToVisible(row + 1); + previewPanel.focusTextPanel(); + } + } + return true; + } + return false; + } + + public void selectBkColorButtonActionPerformed(ActionEvent evt) { + Color newColor = JColorChooser.showDialog(null, AppStrings.translate("dialog.selectbkcolor.title"), View.getSwfBackgroundColor()); + if (newColor != null) { + View.setSwfBackgroundColor(newColor); + reload(true); + } + } + + public void replaceButtonActionPerformed(ActionEvent evt) { + TreeItem item = tagTree.getCurrentTreeItem(); + if (item == null) { + return; + } + + if (item instanceof DefineSoundTag) { + File selectedFile = showImportFileChooser("filter.sounds|*.mp3;*.wav|filter.sounds.mp3|*.mp3|filter.sounds.wav|*.wav"); + if (selectedFile != null) { + File selfile = Helper.fixDialogFile(selectedFile); + DefineSoundTag ds = (DefineSoundTag) item; + int soundFormat = SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN; + if (selfile.getName().toLowerCase().endsWith(".mp3")) { + soundFormat = SoundFormat.FORMAT_MP3; + } + + boolean ok = false; + try { + ok = ds.setSound(new FileInputStream(selfile), soundFormat); + } catch (IOException ex) { + //ignore + } + + if (!ok) { + View.showMessageDialog(null, translate("error.sound.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); + } else { + reload(true); + } + } + } + if (item instanceof ImageTag) { + ImageTag it = (ImageTag) item; + if (it.importSupported()) { + File selectedFile = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp"); + if (selectedFile != null) { + File selfile = Helper.fixDialogFile(selectedFile); + byte[] data = Helper.readFile(selfile.getAbsolutePath()); + try { + Tag newTag = new ImageImporter().importImage(it, data); + SWF swf = it.getSwf(); + if (newTag != null) { + refreshTree(swf); + setTagTreeSelectedNode(newTag); + } + swf.clearImageCache(); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Invalid image", ex); + View.showMessageDialog(null, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); + } + + reload(true); + } + } + } + if (item instanceof ShapeTag) { + ShapeTag st = (ShapeTag) item; + File selectedFile = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp"); + if (selectedFile != null) { + File selfile = Helper.fixDialogFile(selectedFile); + byte[] data = Helper.readFile(selfile.getAbsolutePath()); + try { + Tag newTag = new ShapeImporter().importImage(st, data); + SWF swf = st.getSwf(); + if (newTag != null) { + refreshTree(swf); + setTagTreeSelectedNode(newTag); + } + + swf.clearImageCache(); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Invalid image", ex); + View.showMessageDialog(null, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); + } + reload(true); + } + } + if (item instanceof DefineBinaryDataTag) { + DefineBinaryDataTag bt = (DefineBinaryDataTag) item; + File selectedFile = showImportFileChooser(""); + if (selectedFile != null) { + File selfile = Helper.fixDialogFile(selectedFile); + byte[] data = Helper.readFile(selfile.getAbsolutePath()); + new BinaryDataImporter().importData(bt, data); + refreshTree(bt.getSwf()); + reload(true); + } + } + } + + public void replaceAlphaButtonActionPerformed(ActionEvent evt) { + TreeItem item = tagTree.getCurrentTreeItem(); + if (item == null) { + return; + } + + if (item instanceof DefineBitsJPEG3Tag || item instanceof DefineBitsJPEG4Tag) { + ImageTag it = (ImageTag) item; + if (it.importSupported()) { + File selectedFile = showImportFileChooser(""); + if (selectedFile != null) { + File selfile = Helper.fixDialogFile(selectedFile); + byte[] data = Helper.readFile(selfile.getAbsolutePath()); + try { + new ImageImporter().importImageAlpha(it, data); + SWF swf = it.getSwf(); + swf.clearImageCache(); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Invalid alpha channel data", ex); + View.showMessageDialog(null, translate("error.image.alpha.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); + } + + reload(true); + } + } + } + } + + public void exportJavaSourceActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + exportJavaSource(); + } + + public void exportSwfXmlActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + exportSwfXml(); + } + + public void importSwfXmlActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + importSwfXml(); + } + + public void exportSelectionActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + export(true); + } + + public File showImportFileChooser(String filter) { + String[] filterArray = filter.length() > 0 ? filter.split("\\|") : new String[0]; + + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); + boolean first = true; + for (int i = 0; i < filterArray.length; i += 2) { + final String filterName = filterArray[i]; + final String[] extensions = filterArray[i + 1].split(";"); + for (int j = 0; j < extensions.length; j++) { + if (extensions[j].startsWith("*.")) { + extensions[j] = extensions[j].substring(1); + } + } + FileFilter ff = new FileFilter() { + + @Override + public boolean accept(File f) { + if (f.isDirectory()) { + return true; + } + String fileName = f.getName().toLowerCase(); + for (String ext : extensions) { + if (fileName.endsWith(ext)) { + return true; + } + } + return false; + } + + @Override + public String getDescription() { + return translate(filterName); + } + }; + if (first) { + fc.setFileFilter(ff); + } else { + fc.addChoosableFileFilter(ff); + } + first = false; + } + + JFrame f = new JFrame(); + View.setWindowIcon(f); + if (fc.showOpenDialog(f) == JFileChooser.APPROVE_OPTION) { + File result = fc.getSelectedFile(); + Configuration.lastOpenDir.set(Helper.fixDialogFile(result).getParentFile().getAbsolutePath()); + return result; + } + + return null; + } + + private void showDetail(String card) { + CardLayout cl = (CardLayout) (detailPanel.getLayout()); + cl.show(detailPanel, card); + if (card.equals(DETAILCARDEMPTYPANEL)) { + if (detailPanel.isVisible()) { + detailPanel.setVisible(false); + } + } else { + if (!detailPanel.isVisible()) { + detailPanel.setVisible(true); + } + } + } + + private void showCard(String card) { + CardLayout cl = (CardLayout) (displayPanel.getLayout()); + cl.show(displayPanel, card); + } + + @Override + public void valueChanged(TreeSelectionEvent e) { + Object source = e.getSource(); + if (source == dumpTree) { + reload(false); + return; + } + + TreeItem treeItem = (TreeItem) e.getPath().getLastPathComponent(); + if (!(treeItem instanceof SWFList)) { + SWF swf = treeItem.getSwf(); + if (swfs.isEmpty()) { + // show welcome panel after closing swfs + updateUi(); + } else { + if (swf == null) { + swf = swfs.get(0).get(0); + } + + updateUi(swf); + } + } else { + updateUi(); + } + + reload(false); + } + + public void unloadFlashPlayer() { + if (flashPanel != null) { + try { + flashPanel.close(); + } catch (IOException ex) { + // ignore + } + } + } + + private void stopFlashPlayer() { + if (flashPanel != null) { + if (!flashPanel.isStopped()) { + flashPanel.stopSWF(); + } + } + } + + public boolean isInternalFlashViewerSelected() { + return mainMenu.isInternalFlashViewerSelected(); + } + + public static final int VIEW_RESOURCES = 0; + + public static final int VIEW_DUMP = 1; + + public static final int VIEW_TIMELINE = 2; + + private int getCurrentView() { + return Configuration.dumpView.get() ? VIEW_DUMP : VIEW_RESOURCES; + } + + public void setTreeModel(int view) { + switch (view) { + case VIEW_DUMP: + if (dumpTree.getModel() == null) { + DumpTreeModel dtm = new DumpTreeModel(swfs); + dumpTree.setModel(dtm); + dumpTree.expandFirstLevelNodes(); + } + break; + case VIEW_RESOURCES: + if (tagTree.getModel() == null) { + TagTreeModel ttm = new TagTreeModel(swfs, Configuration.tagTreeShowEmptyFolders.get()); + tagTree.setModel(ttm); + tagTree.expandFirstLevelNodes(); + } + break; + } + } + + public boolean showView(int view) { + + CardLayout cl = (CardLayout) (contentPanel.getLayout()); + setTreeModel(view); + switch (view) { + case VIEW_DUMP: + if (!isWelcomeScreen) { + cl.show(contentPanel, SPLIT_PANE1); + } + treePanel.removeAll(); + treePanel.add(new JScrollPane(dumpTree), BorderLayout.CENTER); + treePanelMode = TreePanelMode.DUMP_TREE; + showDetail(DETAILCARDEMPTYPANEL); + reload(true); + treePanel.revalidate(); + return true; + case VIEW_RESOURCES: + if (!isWelcomeScreen) { + cl.show(contentPanel, SPLIT_PANE1); + } + treePanel.removeAll(); + treePanel.add(new JScrollPane(tagTree), BorderLayout.CENTER); + treePanel.add(searchPanel, BorderLayout.SOUTH); + treePanelMode = TreePanelMode.TAG_TREE; + + treePanel.addComponentListener(new ComponentAdapter() { + + @Override + public void componentResized(ComponentEvent e) { + tagTree.scrollPathToVisible(tagTree.getSelectionPath()); + } + + }); + + reload(true); + treePanel.revalidate(); + return true; + case VIEW_TIMELINE: + final SWF swf = getCurrentSwf(); + if (swf != null) { + TreeItem item = tagTree.getCurrentTreeItem(); + if (item instanceof TagScript) { + item = ((TagScript) item).getTag(); + } + if (item instanceof Timelined) { + timelineViewPanel.setTimelined((Timelined) item); + } else { + timelineViewPanel.setTimelined(swf); + } + cl.show(contentPanel, TIMELINE_PANEL); + return true; + } + return false; + } + return false; + + } + + private void dumpViewReload(boolean forceReload) { + showDetail(DETAILCARDEMPTYPANEL); + + DumpInfo dumpInfo = (DumpInfo) dumpTree.getLastSelectedPathComponent(); + if (dumpInfo == null) { + showCard(CARDEMPTYPANEL); + return; + } + + dumpViewPanel.revalidate(); + dumpViewPanel.setSelectedNode(dumpInfo); + showCard(CARDDUMPVIEW); + } + + public void loadFromBinaryTag(final DefineBinaryDataTag binaryDataTag) { + loadFromBinaryTag(Arrays.asList(binaryDataTag)); + } + + public void loadFromBinaryTag(final List binaryDataTags) { + + Main.loadingDialog.setVisible(true); + new CancellableWorker() { + + @Override + protected Void doInBackground() throws Exception { + try { + for (DefineBinaryDataTag binaryDataTag : binaryDataTags) { + try { + InputStream is = new ByteArrayInputStream(binaryDataTag.binaryData.getRangeData()); + SWF bswf = new SWF(is, null, "(SWF Data)", new ProgressListener() { + + @Override + public void progress(int p) { + Main.loadingDialog.setPercent(p); + } + }, Configuration.parallelSpeedUp.get()); + binaryDataTag.innerSwf = bswf; + bswf.binaryData = binaryDataTag; + } catch (IOException ex) { + //ignore + } + } + } catch (InterruptedException ex) { + //ignore + } + + return null; + } + + @Override + protected void onStart() { + Main.startWork(AppStrings.translate("work.reading.swf") + "...", this); + } + + @Override + protected void done() { + View.execInEventDispatch(() -> { + Main.loadingDialog.setVisible(false); + Main.stopWork(); + }); + } + }.execute(); + } + + private void closeTag() { + previewPanel.closeTag(); + } + + public void reload(boolean forceReload) { + tagTree.scrollPathToVisible(tagTree.getSelectionPath()); + if (Configuration.dumpView.get()) { + dumpViewReload(forceReload); + return; + } + + TreeItem treeItem = null; + TreePath treePath = tagTree.getSelectionPath(); + if (treePath != null && tagTree.getModel().treePathExists(treePath)) { + treeItem = (TreeItem) treePath.getLastPathComponent(); + } + + // save last selected node to config + if (treeItem != null) { + SWF swf = treeItem.getSwf(); + if (swf != null) { + swf = swf.getRootSwf(); + } + + if (swf != null) { + SwfSpecificConfiguration swfConf = Configuration.getOrCreateSwfSpecificConfiguration(swf.getShortFileName()); + swfConf.lastSelectedPath = tagTree.getSelectionPathString(); + } + } + + if (!forceReload && (treeItem == oldItem)) { + return; + } + + if (oldItem != treeItem) { + closeTag(); + } + + oldItem = treeItem; + + // show the preview of the tag when the user clicks to the tagname inside the scripts node, too + // this is a little bit inconsistent, beacuse the frames (FrameScript) are not shown + boolean preferScript = false; + if (treeItem instanceof TagScript) { + treeItem = ((TagScript) treeItem).getTag(); + preferScript = true; + } + + folderPreviewPanel.clear(); + previewPanel.clear(); + stopFlashPlayer(); + + previewPanel.setImageReplaceButtonVisible(false, false); + + boolean internalViewer = isInternalFlashViewerSelected(); + + if (treeItem instanceof ScriptPack) { + final ScriptPack scriptLeaf = (ScriptPack) treeItem; + if (setSourceWorker != null) { + setSourceWorker.cancel(true); + setSourceWorker = null; + } + if (!Main.isWorking()) { + CancellableWorker worker = new CancellableWorker() { + + @Override + protected Void doInBackground() throws Exception { + ABCPanel abcPanel = getABCPanel(); + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.clear(); + abcPanel.setAbc(scriptLeaf.abc); + abcPanel.decompiledTextArea.setScript(scriptLeaf); + abcPanel.decompiledTextArea.setNoTrait(); + return null; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.decompiling") + "...", this); + } + + @Override + protected void done() { + View.execInEventDispatch(() -> { + setSourceWorker = null; + try { + get(); + } catch (CancellationException ex) { + getABCPanel().decompiledTextArea.setText("// " + AppStrings.translate("work.canceled")); + } catch (Exception ex) { + Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, "Error", ex); + getABCPanel().decompiledTextArea.setText("// " + AppStrings.translate("decompilationError") + ": " + ex); + } + + Main.stopWork(); + }); + } + }; + + worker.execute(); + setSourceWorker = worker; + } + + showDetail(DETAILCARDAS3NAVIGATOR); + showCard(CARDACTIONSCRIPT3PANEL); + return; + } + + if (treeItem instanceof Tag) { + Tag tag = (Tag) treeItem; + TagInfo tagInfo = new TagInfo(); + tag.getTagInfo(tagInfo); + if (!tagInfo.isEmpty()) { + tagInfoPanel.setTagInfos(tagInfo); + showDetail(DETAILCARDTAGINFO); + } else { + showDetail(DETAILCARDEMPTYPANEL); + } + } else { + showDetail(DETAILCARDEMPTYPANEL); + } + + if (treeItem instanceof HeaderItem) { + headerPanel.load(((HeaderItem) treeItem).getSwf()); + showCard(CARDHEADER); + } else if (treeItem instanceof FolderItem) { + showFolderPreview((FolderItem) treeItem); + } else if (treeItem instanceof SWF) { + SWF swf = (SWF) treeItem; + if (internalViewer) { + previewPanel.showImagePanel(swf, swf, -1); + } else { + previewPanel.setParametersPanelVisible(false); + if (flashPanel != null) { + previewPanel.showFlashViewerPanel(); + previewPanel.showSwf(swf); + } + } + + showCard(CARDPREVIEWPANEL); + } else if (treeItem instanceof MetadataTag) { + MetadataTag metadataTag = (MetadataTag) treeItem; + previewPanel.showMetaDataPanel(metadataTag); + showCard(CARDPREVIEWPANEL); + } else if (treeItem instanceof DefineBinaryDataTag) { + DefineBinaryDataTag binaryTag = (DefineBinaryDataTag) treeItem; + previewPanel.showBinaryPanel(binaryTag); + showCard(CARDPREVIEWPANEL); + } else if (treeItem instanceof ASMSource && (!(treeItem instanceof DrawableTag) || preferScript)) { + getActionPanel().setSource((ASMSource) treeItem, !forceReload); + showCard(CARDACTIONSCRIPTPANEL); + } else if (treeItem instanceof ImageTag) { + ImageTag imageTag = (ImageTag) treeItem; + previewPanel.setImageReplaceButtonVisible(imageTag.importSupported(), imageTag instanceof DefineBitsJPEG3Tag || imageTag instanceof DefineBitsJPEG4Tag); + previewPanel.showImagePanel(imageTag.getImage()); + showCard(CARDPREVIEWPANEL); + } else if ((treeItem instanceof DrawableTag) && (!(treeItem instanceof TextTag)) && (!(treeItem instanceof FontTag)) && internalViewer) { + final Tag tag = (Tag) treeItem; + DrawableTag d = (DrawableTag) tag; + Timelined timelined; + if (treeItem instanceof Timelined && !(treeItem instanceof ButtonTag)) { + timelined = (Timelined) tag; + } else { + timelined = makeTimelined(tag); + } + + previewPanel.setParametersPanelVisible(false); + previewPanel.showImagePanel(timelined, tag.getSwf(), -1); + showCard(CARDPREVIEWPANEL); + } else if ((treeItem instanceof FontTag) && internalViewer) { + showFontTag((FontTag) treeItem); + } else if ((treeItem instanceof TextTag) && internalViewer) { + showTextTag((TextTag) treeItem); + } else if (treeItem instanceof Frame && internalViewer) { + Frame fn = (Frame) treeItem; + SWF swf = fn.getSwf(); + List controlTags = swf.tags; + int containerId = 0; + RECT rect = swf.displayRect; + int totalFrameCount = swf.frameCount; + Timelined timelined = swf; + if (fn.timeline.timelined instanceof DefineSpriteTag) { + DefineSpriteTag parentSprite = (DefineSpriteTag) fn.timeline.timelined; + controlTags = parentSprite.subTags; + containerId = parentSprite.spriteId; + rect = parentSprite.getRect(); + totalFrameCount = parentSprite.frameCount; + timelined = parentSprite; + } + + previewPanel.showImagePanel(timelined, swf, fn.frame); + showCard(CARDPREVIEWPANEL); + } else if ((treeItem instanceof SoundTag)) { //&& isInternalFlashViewerSelected() && (Arrays.asList("mp3", "wav").contains(((SoundTag) tagObj).getExportFormat())))) { + previewPanel.showImagePanel(new SerializableImage(View.loadImage("sound32"))); + previewPanel.setImageReplaceButtonVisible(treeItem instanceof DefineSoundTag, false); + try { + SoundTagPlayer soundThread = new SoundTagPlayer((SoundTag) treeItem, Configuration.loopMedia.get() ? Integer.MAX_VALUE : 1, true); + previewPanel.setMedia(soundThread); + } catch (LineUnavailableException | IOException | UnsupportedAudioFileException ex) { + logger.log(Level.SEVERE, null, ex); + } + + showCard(CARDPREVIEWPANEL); + } else if ((treeItem instanceof Frame) || ((treeItem instanceof CharacterTag) || (treeItem instanceof FontTag)) && (treeItem instanceof Tag) || (treeItem instanceof SoundStreamHeadTypeTag)) { + previewPanel.createAndShowTempSwf(treeItem); + + if (treeItem instanceof TextTag) { + showTextTag((TextTag) treeItem); + } else if (treeItem instanceof FontTag) { + showFontTag((FontTag) treeItem); + } else { + previewPanel.setParametersPanelVisible(false); + } + + showCard(CARDPREVIEWPANEL); + } else if (treeItem instanceof Tag) { + showGenericTag((Tag) treeItem); + } else { + showCard(CARDEMPTYPANEL); + } + } + + public void repaintTree() { + tagTree.repaint(); + reload(true); + } + + public void showGenericTag(Tag tag) { + previewPanel.showGenericTagPanel(tag); + showCard(CARDPREVIEWPANEL); + } + + private void showFontTag(FontTag ft) { + + previewPanel.showFontPanel(ft); + showCard(CARDPREVIEWPANEL); + } + + private void showTextTag(TextTag textTag) { + + previewPanel.showTextPanel(textTag); + showCard(CARDPREVIEWPANEL); + } + + public void showTextTagWithNewValue(TextTag textTag, TextTag newTextTag) { + + previewPanel.showTextComparePanel(textTag, newTextTag); + } + + private void showFolderPreview(FolderItem item) { + List folderPreviewItems = new ArrayList<>(); + String folderName = item.getName(); + SWF swf = item.swf; + switch (folderName) { + case TagTreeModel.FOLDER_SHAPES: + for (Tag tag : swf.tags) { + if (tag instanceof ShapeTag) { + folderPreviewItems.add(tag); + } + } + break; + case TagTreeModel.FOLDER_MORPHSHAPES: + for (Tag tag : swf.tags) { + if (tag instanceof MorphShapeTag) { + folderPreviewItems.add(tag); + } + } + break; + case TagTreeModel.FOLDER_SPRITES: + for (Tag tag : swf.tags) { + if (tag instanceof DefineSpriteTag) { + folderPreviewItems.add(tag); + } + } + break; + case TagTreeModel.FOLDER_BUTTONS: + for (Tag tag : swf.tags) { + if (tag instanceof ButtonTag) { + folderPreviewItems.add(tag); + } + } + break; + case TagTreeModel.FOLDER_FONTS: + for (Tag tag : swf.tags) { + if (tag instanceof FontTag) { + folderPreviewItems.add(tag); + } + } + break; + case TagTreeModel.FOLDER_FRAMES: + for (Frame frame : swf.getTimeline().getFrames()) { + folderPreviewItems.add(frame); + } + break; + case TagTreeModel.FOLDER_IMAGES: + for (Tag tag : swf.tags) { + if (tag instanceof ImageTag) { + folderPreviewItems.add(tag); + } + } + break; + case TagTreeModel.FOLDER_TEXTS: + for (Tag tag : swf.tags) { + if (tag instanceof TextTag) { + folderPreviewItems.add(tag); + } + } + break; + } + + folderPreviewPanel.setItems(folderPreviewItems); + showCard(CARDFOLDERPREVIEWPANEL); + } + + private boolean isFreeing; + + @Override + public boolean isFreeing() { + return isFreeing; + } + + @Override + public void free() { + isFreeing = true; + } + + public void setErrorState(ErrorState errorState) { + statusPanel.setErrorState(errorState); + } + + public static Timelined makeTimelined(final Tag tag) { + return makeTimelined(tag, -1); + } + + public static Timelined makeTimelined(final Tag tag, final int fontFrameNum) { + + return new Timelined() { + + private Timeline tim; + + @Override + public Timeline getTimeline() { + if (tim != null) { + return tim; + } + tim = new Timeline(tag.getSwf(), null, new ArrayList<>(), ((CharacterTag) tag).getCharacterId(), getRect()); + initTimeline(tim); + return tim; + } + + @Override + public void resetTimeline() { + if (tim != null) { + tim.reset(tag.getSwf(), null, new ArrayList<>(), ((CharacterTag) tag).getCharacterId(), getRect()); + initTimeline(tim); + } + } + + private void initTimeline(Timeline timeline) { + if (tag instanceof MorphShapeTag) { + tim.frameRate = MORPH_SHAPE_ANIMATION_FRAME_RATE; + int framesCnt = tim.frameRate * MORPH_SHAPE_ANIMATION_LENGTH; + for (int i = 0; i < framesCnt; i++) { + Frame f = new Frame(tim, i); + DepthState ds = new DepthState(tag.getSwf(), f); + ds.characterId = ((CharacterTag) tag).getCharacterId(); + ds.matrix = new MATRIX(); + ds.ratio = i * 65535 / framesCnt; + f.layers.put(1, ds); + f.layersChanged = true; + tim.addFrame(f); + } + } else if (tag instanceof FontTag) { + int pageCount = PreviewPanel.getFontPageCount((FontTag) tag); + int frame = fontFrameNum; + if (frame < 0 || frame >= pageCount) { + frame = 0; + } + + Frame f = new Frame(tim, 0); + DepthState ds = new DepthState(tag.getSwf(), f); + ds.characterId = ((CharacterTag) tag).getCharacterId(); + ds.matrix = new MATRIX(); + f.layers.put(1, ds); + f.layersChanged = true; + tim.addFrame(f); + tim.fontFrameNum = frame; + } else { + Frame f = new Frame(tim, 0); + DepthState ds = new DepthState(tag.getSwf(), f); + ds.characterId = ((CharacterTag) tag).getCharacterId(); + ds.matrix = new MATRIX(); + f.layers.put(1, ds); + tim.addFrame(f); + } + tim.displayRect = getRect(); + } + + @Override + public RECT getRect() { + return getRect(new HashSet<>()); + } + + @Override + public RECT getRect(Set added) { + BoundedTag bt = (BoundedTag) tag; + if (!added.contains(bt)) { + return bt.getRect(added); + } + return new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); + } + + @Override + public int hashCode() { + return tag.hashCode(); + } + }; + } + + private void disposeInner(Container container) { + for (Component c : container.getComponents()) { + if (c instanceof Container) { + Container c2 = (Container) c; + disposeInner(c2); + } + } + + container.removeAll(); + container.setLayout(null); + if (container instanceof TagEditorPanel) { + Helper.emptyObject(container); + } + } + + public void dispose() { + setDropTarget(null); + disposeInner(this); + Helper.emptyObject(this); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index 67c74c0e3..b8d651eca 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -1,1189 +1,1189 @@ -/* - * Copyright (C) 2010-2015 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.SWFOutputStream; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.parser.ActionParseException; -import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; -import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; -import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; -import com.jpexs.decompiler.flash.gui.player.MediaDisplay; -import com.jpexs.decompiler.flash.gui.player.PlayerControls; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineBitsTag; -import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; -import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; -import com.jpexs.decompiler.flash.tags.DefineSoundTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DefineTextTag; -import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; -import com.jpexs.decompiler.flash.tags.DoActionTag; -import com.jpexs.decompiler.flash.tags.DoInitActionTag; -import com.jpexs.decompiler.flash.tags.EndTag; -import com.jpexs.decompiler.flash.tags.ExportAssetsTag; -import com.jpexs.decompiler.flash.tags.JPEGTablesTag; -import com.jpexs.decompiler.flash.tags.MetadataTag; -import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; -import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.VideoFrameTag; -import com.jpexs.decompiler.flash.tags.base.AloneTag; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.timeline.TagScript; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.flash.types.GLYPHENTRY; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.decompiler.flash.types.TEXTRECORD; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.SerializableImage; -import java.awt.BorderLayout; -import java.awt.CardLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Font; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.StringReader; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.SwingConstants; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; - -/** - * - * @author JPEXS - */ -public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel { - - private static final String FLASH_VIEWER_CARD = "FLASHVIEWER"; - - private static final String DRAW_PREVIEW_CARD = "DRAWPREVIEW"; - - private static final String GENERIC_TAG_CARD = "GENERICTAG"; - - private static final String BINARY_TAG_CARD = "BINARYTAG"; - - private static final String METADATA_TAG_CARD = "METADATATAG"; - - private static final String CARDTEXTPANEL = "Text card"; - - private static final String CARDFONTPANEL = "Font card"; - - private final MainPanel mainPanel; - - private final JPanel viewerCards; - - private final FlashPlayerPanel flashPanel; - - private File tempFile; - - private ImagePanel imagePanel; - - private PlayerControls imagePlayControls; - - private MediaDisplay media; - - private BinaryPanel binaryPanel; - - private LineMarkedEditorPane metadataEditor; - - private GenericTagPanel genericTagPanel; - - private JPanel displayWithPreview; - - // Image tag buttons - private JButton replaceImageButton; - - private JButton replaceImageAlphaButton; - - private JButton prevFontsButton; - - private JButton nextFontsButton; - - // Binary tag buttons - private JButton replaceBinaryButton; - - // Metadata editor buttons - private JButton metadataEditButton; - - private JButton metadataSaveButton; - - private JButton metadataCancelButton; - - // Generic tag buttons - private JButton genericEditButton; - - private JButton genericSaveButton; - - private JButton genericCancelButton; - - private JPanel parametersPanel; - - private FontPanel fontPanel; - - private int fontPageNum; - - private TextPanel textPanel; - - private MetadataTag metadataTag; - - public PreviewPanel(MainPanel mainPanel, FlashPlayerPanel flashPanel) { - super(JSplitPane.HORIZONTAL_SPLIT, Configuration.guiPreviewSplitPaneDividerLocationPercent); - this.mainPanel = mainPanel; - this.flashPanel = flashPanel; - - viewerCards = new JPanel(); - viewerCards.setLayout(new CardLayout()); - - viewerCards.add(createFlashPlayerPanel(flashPanel), FLASH_VIEWER_CARD); - viewerCards.add(createImagesCard(), DRAW_PREVIEW_CARD); - viewerCards.add(createBinaryCard(), BINARY_TAG_CARD); - viewerCards.add(createMetadataCard(), METADATA_TAG_CARD); - viewerCards.add(createGenericTagCard(), GENERIC_TAG_CARD); - setLeftComponent(viewerCards); - - createParametersPanel(); - - showCardLeft(FLASH_VIEWER_CARD); - } - - private void createParametersPanel() { - displayWithPreview = new JPanel(new CardLayout()); - - textPanel = new TextPanel(mainPanel); - displayWithPreview.add(textPanel, CARDTEXTPANEL); - - fontPanel = new FontPanel(mainPanel); - displayWithPreview.add(fontPanel, CARDFONTPANEL); - - JLabel paramsLabel = new HeaderLabel(mainPanel.translate("parameters")); - paramsLabel.setHorizontalAlignment(SwingConstants.CENTER); - //paramsLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - - parametersPanel = new JPanel(new BorderLayout()); - parametersPanel.add(paramsLabel, BorderLayout.NORTH); - parametersPanel.add(displayWithPreview, BorderLayout.CENTER); - setRightComponent(parametersPanel); - } - - private JPanel createImageButtonsPanel() { - replaceImageButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("replaceimage16")); - replaceImageButton.setMargin(new Insets(3, 3, 3, 10)); - replaceImageButton.addActionListener(mainPanel::replaceButtonActionPerformed); - replaceImageButton.setVisible(false); - - replaceImageAlphaButton = new JButton(mainPanel.translate("button.replaceAlphaChannel"), View.getIcon("replacealpha16")); - replaceImageAlphaButton.setMargin(new Insets(3, 3, 3, 10)); - replaceImageAlphaButton.addActionListener(mainPanel::replaceAlphaButtonActionPerformed); - replaceImageAlphaButton.setVisible(false); - - prevFontsButton = new JButton(mainPanel.translate("button.prev"), View.getIcon("prev16")); - prevFontsButton.setMargin(new Insets(3, 3, 3, 10)); - prevFontsButton.addActionListener(this::prevFontsButtonActionPerformed); - prevFontsButton.setVisible(false); - - nextFontsButton = new JButton(mainPanel.translate("button.next"), View.getIcon("next16")); - nextFontsButton.setMargin(new Insets(3, 3, 3, 10)); - nextFontsButton.addActionListener(this::nextFontsButtonActionPerformed); - nextFontsButton.setVisible(false); - - ButtonsPanel imageButtonsPanel = new ButtonsPanel(); - imageButtonsPanel.add(replaceImageButton); - imageButtonsPanel.add(replaceImageAlphaButton); - imageButtonsPanel.add(prevFontsButton); - imageButtonsPanel.add(nextFontsButton); - return imageButtonsPanel; - } - - private JPanel createBinaryButtonsPanel() { - replaceBinaryButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("edit16")); - replaceBinaryButton.setMargin(new Insets(3, 3, 3, 10)); - replaceBinaryButton.addActionListener(mainPanel::replaceButtonActionPerformed); - - ButtonsPanel binaryButtonsPanel = new ButtonsPanel(); - binaryButtonsPanel.add(replaceBinaryButton); - return binaryButtonsPanel; - } - - private JPanel createGenericTagButtonsPanel() { - genericEditButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16")); - genericEditButton.setMargin(new Insets(3, 3, 3, 10)); - genericEditButton.addActionListener(this::editGenericTagButtonActionPerformed); - genericSaveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); - genericSaveButton.setMargin(new Insets(3, 3, 3, 10)); - genericSaveButton.addActionListener(this::saveGenericTagButtonActionPerformed); - genericSaveButton.setVisible(false); - genericCancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); - genericCancelButton.setMargin(new Insets(3, 3, 3, 10)); - genericCancelButton.addActionListener(this::cancelGenericTagButtonActionPerformed); - genericCancelButton.setVisible(false); - - ButtonsPanel genericTagButtonsPanel = new ButtonsPanel(); - genericTagButtonsPanel.add(genericEditButton); - genericTagButtonsPanel.add(genericSaveButton); - genericTagButtonsPanel.add(genericCancelButton); - return genericTagButtonsPanel; - } - - private JPanel createMetadataButtonsPanel() { - metadataEditButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16")); - metadataEditButton.setMargin(new Insets(3, 3, 3, 10)); - metadataEditButton.addActionListener(this::editMetadataButtonActionPerformed); - metadataSaveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); - metadataSaveButton.setMargin(new Insets(3, 3, 3, 10)); - metadataSaveButton.addActionListener(this::saveMetadataButtonActionPerformed); - metadataSaveButton.setVisible(false); - metadataCancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); - metadataCancelButton.setMargin(new Insets(3, 3, 3, 10)); - metadataCancelButton.addActionListener(this::cancelMetadataButtonActionPerformed); - metadataCancelButton.setVisible(false); - - ButtonsPanel metadataTagButtonsPanel = new ButtonsPanel(); - metadataTagButtonsPanel.add(metadataEditButton); - metadataTagButtonsPanel.add(metadataSaveButton); - metadataTagButtonsPanel.add(metadataCancelButton); - return metadataTagButtonsPanel; - } - - private JPanel createFlashPlayerPanel(FlashPlayerPanel flashPanel) { - JPanel pan = new JPanel(new BorderLayout()); - JLabel prevLabel = new HeaderLabel(mainPanel.translate("swfpreview")); - prevLabel.setHorizontalAlignment(SwingConstants.CENTER); - //prevLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - - pan.add(prevLabel, BorderLayout.NORTH); - - Component leftComponent; - if (flashPanel != null) { - JPanel flashPlayPanel = new JPanel(new BorderLayout()); - flashPlayPanel.add(flashPanel, BorderLayout.CENTER); - - /*JPanel bottomPanel = new JPanel(new BorderLayout()); - JPanel buttonsPanel = new JPanel(new FlowLayout()); - JButton selectColorButton = new JButton(View.getIcon("color16")); - selectColorButton.addActionListener(mainPanel::selectBkColor); - selectColorButton.setToolTipText(AppStrings.translate("button.selectbkcolor.hint")); - buttonsPanel.add(selectColorButton); - bottomPanel.add(buttonsPanel, BorderLayout.EAST); - - flashPlayPanel.add(bottomPanel, BorderLayout.SOUTH);*/ - JPanel flashPlayPanel2 = new JPanel(new BorderLayout()); - flashPlayPanel2.add(flashPlayPanel, BorderLayout.CENTER); - flashPlayPanel2.add(new PlayerControls(mainPanel, flashPanel), BorderLayout.SOUTH); - leftComponent = flashPlayPanel2; - } else { - JPanel swtPanel = new JPanel(new BorderLayout()); - swtPanel.add(new JLabel("
" + mainPanel.translate("notavailonthisplatform") + "
", JLabel.CENTER), BorderLayout.CENTER); - swtPanel.setBackground(View.getDefaultBackgroundColor()); - leftComponent = swtPanel; - } - - pan.add(leftComponent, BorderLayout.CENTER); - return pan; - } - - private JPanel createImagesCard() { - JPanel shapesCard = new JPanel(new BorderLayout()); - JPanel previewPanel = new JPanel(new BorderLayout()); - - JPanel previewCnt = new JPanel(new BorderLayout()); - imagePanel = new ImagePanel(); - imagePanel.setLoop(Configuration.loopMedia.get()); - previewCnt.add(imagePanel, BorderLayout.CENTER); - previewCnt.add(imagePlayControls = new PlayerControls(mainPanel, imagePanel), BorderLayout.SOUTH); - imagePlayControls.setMedia(imagePanel); - previewPanel.add(previewCnt, BorderLayout.CENTER); - JLabel prevIntLabel = new HeaderLabel(mainPanel.translate("swfpreview.internal")); - prevIntLabel.setHorizontalAlignment(SwingConstants.CENTER); - //prevIntLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - previewPanel.add(prevIntLabel, BorderLayout.NORTH); - - shapesCard.add(previewPanel, BorderLayout.CENTER); - - shapesCard.add(createImageButtonsPanel(), BorderLayout.SOUTH); - return shapesCard; - } - - private JPanel createMetadataCard() { - JPanel metadataCard = new JPanel(new BorderLayout()); - metadataEditor = new LineMarkedEditorPane(); - metadataCard.add(new JScrollPane(metadataEditor), BorderLayout.CENTER); - //metadataEditor.setContentType("text/xml"); - metadataEditor.setEditable(false); - - metadataEditor.setFont(new Font("Monospaced", Font.PLAIN, metadataEditor.getFont().getSize())); - metadataEditor.changeContentType("text/xml"); - metadataEditor.addTextChangedListener(this::metadataTextChanged); - - metadataCard.add(createMetadataButtonsPanel(), BorderLayout.SOUTH); - return metadataCard; - } - - private boolean isMetadataModified() { - return metadataSaveButton.isVisible() && metadataSaveButton.isEnabled(); - } - - private void setMetadataModified(boolean value) { - metadataSaveButton.setEnabled(value); - metadataCancelButton.setEnabled(value); - } - - private void metadataTextChanged() { - setMetadataModified(true); - } - - private void updateMetadataButtonsVisibility() { - boolean edit = metadataEditor.isEditable(); - boolean editorMode = Configuration.editorMode.get(); - metadataEditButton.setVisible(!edit); - metadataSaveButton.setVisible(edit); - boolean metadataModified = isMetadataModified(); - metadataCancelButton.setVisible(edit); - metadataCancelButton.setEnabled(metadataModified || !editorMode); - } - - private JPanel createBinaryCard() { - JPanel binaryCard = new JPanel(new BorderLayout()); - binaryPanel = new BinaryPanel(mainPanel); - binaryCard.add(binaryPanel, BorderLayout.CENTER); - binaryCard.add(createBinaryButtonsPanel(), BorderLayout.SOUTH); - return binaryCard; - } - - private JPanel createGenericTagCard() { - JPanel genericTagCard = new JPanel(new BorderLayout()); - genericTagPanel = new GenericTagTreePanel(); - genericTagCard.add(genericTagPanel, BorderLayout.CENTER); - genericTagCard.add(createGenericTagButtonsPanel(), BorderLayout.SOUTH); - return genericTagCard; - } - - private void showCardLeft(String card) { - CardLayout cl = (CardLayout) (viewerCards.getLayout()); - cl.show(viewerCards, card); - } - - private void showCardRight(String card) { - CardLayout cl = (CardLayout) (displayWithPreview.getLayout()); - cl.show(displayWithPreview, card); - } - - public TextPanel getTextPanel() { - return textPanel; - } - - public void setParametersPanelVisible(boolean show) { - parametersPanel.setVisible(show); - } - - public void showFlashViewerPanel() { - parametersPanel.setVisible(false); - showCardLeft(FLASH_VIEWER_CARD); - } - - public void showImagePanel(Timelined timelined, SWF swf, int frame) { - showCardLeft(DRAW_PREVIEW_CARD); - parametersPanel.setVisible(false); - imagePlayControls.setMedia(imagePanel); - imagePanel.setTimelined(timelined, swf, frame); - } - - public void showImagePanel(SerializableImage image) { - showCardLeft(DRAW_PREVIEW_CARD); - parametersPanel.setVisible(false); - imagePlayControls.setMedia(imagePanel); - imagePanel.setImage(image); - } - - public void showTextComparePanel(TextTag textTag, TextTag newTextTag) { - imagePanel.setText(textTag, newTextTag); - } - - public void setMedia(MediaDisplay media) { - this.media = media; - imagePlayControls.setMedia(media); - } - - public void showFontPanel(FontTag fontTag) { - fontPageNum = 0; - showFontPage(fontTag); - - showCardRight(CARDFONTPANEL); - parametersPanel.setVisible(true); - fontPanel.showFontTag(fontTag); - - int pageCount = getFontPageCount(fontTag); - if (pageCount > 1) { - prevFontsButton.setVisible(true); - nextFontsButton.setVisible(true); - } - } - - private void showFontPage(FontTag fontTag) { - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - showImagePanel(MainPanel.makeTimelined(fontTag), fontTag.getSwf(), fontPageNum); - } - } - - public static int getFontPageCount(FontTag fontTag) { - int pageCount = (fontTag.getGlyphShapeTable().size() - 1) / SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW + 1; - if (pageCount < 1) { - pageCount = 1; - } - return pageCount; - } - - public void showTextPanel(TextTag textTag) { - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - showImagePanel(MainPanel.makeTimelined(textTag), textTag.getSwf(), 0); - } - - showCardRight(CARDTEXTPANEL); - parametersPanel.setVisible(true); - textPanel.setText(textTag); - } - - public void focusTextPanel() { - textPanel.focusTextValue(); - } - - public void clear() { - imagePanel.clearAll(); - if (media != null) { - try { - media.close(); - } catch (IOException ex) { - // ignore - } - } - - binaryPanel.setBinaryData(null); - genericTagPanel.clear(); - fontPanel.clear(); - } - - public void closeTag() { - textPanel.closeTag(); - } - - public static String formatMetadata(String input, int indent) { - input = input.replace("> <", "><"); - try { - Source xmlInput = new StreamSource(new StringReader(input)); - StringWriter stringWriter = new StringWriter(); - StreamResult xmlOutput = new StreamResult(stringWriter); - StringWriter sw = new StringWriter(); - xmlOutput.setWriter(sw); - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - transformerFactory.setAttribute("indent-number", indent); - Transformer transformer = transformerFactory.newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "" + indent); - transformer.transform(xmlInput, xmlOutput); - - return xmlOutput.getWriter().toString(); - } catch (IllegalArgumentException | TransformerException e) { - return input; - } - } - - public void showMetaDataPanel(MetadataTag metadataTag) { - showCardLeft(METADATA_TAG_CARD); - this.metadataTag = metadataTag; - metadataEditor.setEditable(Configuration.editorMode.get()); - metadataEditor.setText(formatMetadata(metadataTag.xmlMetadata, 4)); - setMetadataModified(false); - updateMetadataButtonsVisibility(); - parametersPanel.setVisible(false); - } - - public void showBinaryPanel(DefineBinaryDataTag binaryDataTag) { - showCardLeft(BINARY_TAG_CARD); - binaryPanel.setBinaryData(binaryDataTag); - parametersPanel.setVisible(false); - } - - public void showGenericTagPanel(Tag tag) { - showCardLeft(GENERIC_TAG_CARD); - genericEditButton.setVisible(true); - genericSaveButton.setVisible(false); - genericCancelButton.setVisible(false); - genericTagPanel.setEditMode(false, tag); - parametersPanel.setVisible(false); - } - - public void setImageReplaceButtonVisible(boolean show, boolean showAlpha) { - replaceImageButton.setVisible(show); - replaceImageAlphaButton.setVisible(showAlpha); - prevFontsButton.setVisible(false); - nextFontsButton.setVisible(false); - } - - private static Tag classicTag(Tag t) { - if (t instanceof DefineCompactedFont) { - return ((DefineCompactedFont) t).toClassicFont(); - } - return t; - } - - public void createAndShowTempSwf(TreeItem tagObj) { - SWF swf; - try { - if (tempFile != null) { - tempFile.delete(); - } - - tempFile = File.createTempFile("ffdec_view_", ".swf"); - tempFile.deleteOnExit(); - - Color backgroundColor = View.getSwfBackgroundColor(); - - if (tagObj instanceof FontTag) { //Fonts are always black on white - backgroundColor = View.getDefaultBackgroundColor(); - } - - if (tagObj instanceof Frame) { - Frame fn = (Frame) tagObj; - swf = fn.getSwf(); - if (fn.timeline.timelined == swf) { - for (Tag t : swf.tags) { - if (t instanceof SetBackgroundColorTag) { - backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); - break; - } - } - } - } else { - Tag tag = (Tag) tagObj; - swf = tag.getSwf(); - } - - int frameCount = 1; - int frameRate = swf.frameRate; - HashMap videoFrames = new HashMap<>(); - if (tagObj instanceof DefineVideoStreamTag) { - DefineVideoStreamTag vs = (DefineVideoStreamTag) tagObj; - SWF.populateVideoFrames(vs.getCharacterId(), swf.tags, videoFrames); - frameCount = videoFrames.size(); - } - - List soundFrames = new ArrayList<>(); - if (tagObj instanceof SoundStreamHeadTypeTag) { - soundFrames = ((SoundStreamHeadTypeTag) tagObj).getBlocks(); - frameCount = soundFrames.size(); - } - - if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { - frameRate = MainPanel.MORPH_SHAPE_ANIMATION_FRAME_RATE; - frameCount = MainPanel.MORPH_SHAPE_ANIMATION_LENGTH * frameRate; - } - - if (tagObj instanceof DefineSoundTag) { - frameCount = 1; - } - - if (tagObj instanceof DefineSpriteTag) { - frameCount = ((DefineSpriteTag) tagObj).frameCount; - } - - byte[] data; - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - SWFOutputStream sos2 = new SWFOutputStream(baos, SWF.DEFAULT_VERSION); - RECT outrect = new RECT(swf.displayRect); - - if (tagObj instanceof FontTag) { - outrect.Xmin = 0; - outrect.Ymin = 0; - outrect.Xmax = FontTag.PREVIEWSIZE * 20; - outrect.Ymax = FontTag.PREVIEWSIZE * 20; - } - int width = outrect.getWidth(); - int height = outrect.getHeight(); - - sos2.writeRECT(outrect); - sos2.writeUI8(0); - sos2.writeUI8(frameRate); - sos2.writeUI16(frameCount); //framecnt - - /*FileAttributesTag fa = new FileAttributesTag(); - sos2.writeTag(fa); - */ - new SetBackgroundColorTag(swf, new RGB(backgroundColor)).writeTag(sos2); - - if (tagObj instanceof Frame) { - Frame fn = (Frame) tagObj; - Timelined parent = fn.timeline.timelined; - List subs = fn.timeline.tags; - List doneCharacters = new ArrayList<>(); - int frameCnt = 0; - for (Tag t : subs) { - if (t instanceof ShowFrameTag) { - frameCnt++; - continue; - } - if (frameCnt > fn.frame) { - break; - } - - if (t instanceof DoActionTag || t instanceof DoInitActionTag) { - // todo: Maybe DoABC tags should be removed, too - continue; - } - - Set needed = new HashSet<>(); - t.getNeededCharactersDeep(needed); - for (int n : needed) { - if (!doneCharacters.contains(n)) { - classicTag(swf.getCharacter(n)).writeTag(sos2); - doneCharacters.add(n); - } - } - if (t instanceof CharacterTag) { - int characterId = ((CharacterTag) t).getCharacterId(); - if (!doneCharacters.contains(characterId)) { - doneCharacters.add(((CharacterTag) t).getCharacterId()); - } - } - classicTag(t).writeTag(sos2); - - if (parent != null) { - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag pot = (PlaceObjectTypeTag) t; - int chid = pot.getCharacterId(); - int depth = pot.getDepth(); - MATRIX mat = pot.getMatrix(); - if (mat == null) { - mat = new MATRIX(); - } - mat = Helper.deepCopy(mat); - if (parent instanceof BoundedTag) { - RECT r = ((BoundedTag) parent).getRect(); - mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; - mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; - } else { - mat.translateX += width / 2; - mat.translateY += height / 2; - } - new PlaceObject2Tag(swf, false, false, false, false, false, true, false, true, depth, chid, mat, null, 0, null, 0, null).writeTag(sos2); - - } - } - } - new ShowFrameTag(swf).writeTag(sos2); - } else { - - boolean isSprite = false; - if (tagObj instanceof DefineSpriteTag) { - isSprite = true; - } - int chtId = 0; - if (tagObj instanceof CharacterTag) { - chtId = ((CharacterTag) tagObj).getCharacterId(); - } - - if (tagObj instanceof DefineBitsTag) { - JPEGTablesTag jtt = swf.getJtt(); - if (jtt != null) { - jtt.writeTag(sos2); - } - } else if (tagObj instanceof AloneTag) { - } else { - Set needed = new HashSet<>(); - ((Tag) tagObj).getNeededCharactersDeep(needed); - for (int n : needed) { - if (isSprite && chtId == n) { - continue; - } - - CharacterTag characterTag = swf.getCharacter(n); - if (characterTag instanceof DefineBitsTag) { - JPEGTablesTag jtt = swf.getJtt(); - if (jtt != null) { - jtt.writeTag(sos2); - } - } - - classicTag(characterTag).writeTag(sos2); - } - } - - classicTag((Tag) tagObj).writeTag(sos2); - - MATRIX mat = new MATRIX(); - mat.hasRotate = false; - mat.hasScale = false; - mat.translateX = 0; - mat.translateY = 0; - if (tagObj instanceof BoundedTag) { - RECT r = ((BoundedTag) tagObj).getRect(); - mat.translateX = -r.Xmin; - mat.translateY = -r.Ymin; - mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; - mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; - } else { - mat.translateX = width / 4; - mat.translateY = height / 4; - } - if (tagObj instanceof FontTag) { - - FontTag ft = (FontTag) classicTag((Tag) tagObj); - - int countGlyphsTotal = ft.getGlyphShapeTable().size(); - int countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal); - int fontId = ft.getFontId(); - int cols = (int) Math.ceil(Math.sqrt(countGlyphs)); - int rows = (int) Math.ceil(((float) countGlyphs) / ((float) cols)); - if (rows == 0) { - rows = 1; - cols = 1; - } - int x = 0; - int y = 0; - int firstGlyphIndex = fontPageNum * SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW; - countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal - firstGlyphIndex); - List shapes = ft.getGlyphShapeTable(); - int maxw = 0; - for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { - RECT b = shapes.get(f).getBounds(); - if (b.Xmin == Integer.MAX_VALUE) { - continue; - } - if (b.Ymin == Integer.MAX_VALUE) { - continue; - } - int w = (int) (b.getWidth() / ft.getDivider()); - if (w > maxw) { - maxw = w; - } - x++; - } - - x = 0; - - int BORDER = 3 * 20; - - int textHeight = height / rows; - - while (maxw * textHeight / 1024.0 > width / cols - 2 * BORDER) { - textHeight--; - } - - MATRIX tmat = new MATRIX(); - for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { - if (x >= cols) { - x = 0; - y++; - } - List rec = new ArrayList<>(); - TEXTRECORD tr = new TEXTRECORD(); - - RECT b = shapes.get(f).getBounds(); - int xmin = b.Xmin == Integer.MAX_VALUE ? 0 : (int) (b.Xmin / ft.getDivider()); - xmin *= textHeight / 1024.0; - int ymin = b.Ymin == Integer.MAX_VALUE ? 0 : (int) (b.Ymin / ft.getDivider()); - ymin *= textHeight / 1024.0; - int w = (int) (b.getWidth() / ft.getDivider()); - w *= textHeight / 1024.0; - int h = (int) (b.getHeight() / ft.getDivider()); - h *= textHeight / 1024.0; - - tr.fontId = fontId; - tr.styleFlagsHasFont = true; - tr.textHeight = textHeight; - tr.xOffset = -xmin; - tr.yOffset = 0; - tr.styleFlagsHasXOffset = true; - tr.styleFlagsHasYOffset = true; - tr.glyphEntries = new ArrayList<>(1); - tr.styleFlagsHasColor = true; - tr.textColor = new RGB(0, 0, 0); - GLYPHENTRY ge = new GLYPHENTRY(); - - double ga = ft.getGlyphAdvance(f); - int cw = ga == -1 ? w : (int) (ga / ft.getDivider() * textHeight / 1024.0); - - ge.glyphAdvance = 0; - ge.glyphIndex = f; - tr.glyphEntries.add(ge); - rec.add(tr); - - tmat.translateX = x * width / cols + width / cols / 2 - w / 2; - tmat.translateY = y * height / rows + height / rows / 2; - new DefineTextTag(swf, 999 + f, new RECT(0, cw, ymin, ymin + h), new MATRIX(), rec).writeTag(sos2); - new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1 + f, 999 + f, tmat, null, 0, null, 0, null).writeTag(sos2); - x++; - } - new ShowFrameTag(swf).writeTag(sos2); - } else if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { - new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - for (int ratio = 0; ratio < 65536; ratio += 65536 / frameCount) { - new PlaceObject2Tag(swf, false, false, false, true, false, true, false, true, 1, chtId, mat, null, ratio, null, 0, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - } else if (tagObj instanceof SoundStreamHeadTypeTag) { - for (SoundStreamBlockTag blk : soundFrames) { - blk.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - } else if (tagObj instanceof DefineSoundTag) { - ExportAssetsTag ea = new ExportAssetsTag(swf); - DefineSoundTag ds = (DefineSoundTag) tagObj; - ea.tags.add(ds.soundId); - ea.names.add("my_define_sound"); - ea.writeTag(sos2); - List actions; - DoActionTag doa; - - doa = new DoActionTag(swf, null); - actions = ASMParser.parse(0, false, - "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\"\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\" 0.0 \"Sound\"\n" - + "NewObject\n" - + "SetMember\n" - + "Push \"my_define_sound\" 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"attachSound\"\n" - + "CallMethod\n" - + "Pop\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - actions = ASMParser.parse(0, false, - "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"start\"\n" - + "StopSounds\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\" 0.0 \"Sound\"\n" - + "NewObject\n" - + "SetMember\n" - + "Push \"my_define_sound\" 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"attachSound\"\n" - + "CallMethod\n" - + "Pop\n" - + "Push 9999 0.0 2 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"start\"\n" - + "CallMethod\n" - + "Pop\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - actions = ASMParser.parse(0, false, - "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"onSoundComplete\" \"start\" \"execParam\"\n" - + "StopSounds\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\" 0.0 \"Sound\"\n" - + "NewObject\n" - + "SetMember\n" - + "Push \"my_define_sound\" 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"attachSound\"\n" - + "CallMethod\n" - + "Pop\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"onSoundComplete\"\n" - + "DefineFunction2 \"\" 0 2 false true true false true false true false false {\n" - + "Push 0.0 register1 \"my_sound\"\n" - + "GetMember\n" - + "Push \"start\"\n" - + "CallMethod\n" - + "Pop\n" - + "}\n" - + "SetMember\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"execParam\"\n" - + "GetMember\n" - + "Push 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"start\"\n" - + "CallMethod\n" - + "Pop\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - actions = ASMParser.parse(0, false, - "StopSounds\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - new ShowFrameTag(swf).writeTag(sos2); - } else if (tagObj instanceof DefineVideoStreamTag) { - - new PlaceObject2Tag(swf, false, false, false, false, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); - List frs = new ArrayList<>(videoFrames.values()); - Collections.sort(frs, new Comparator() { - @Override - public int compare(VideoFrameTag o1, VideoFrameTag o2) { - return o1.frameNum - o2.frameNum; - } - }); - boolean first = true; - int ratio = 0; - for (VideoFrameTag f : frs) { - if (!first) { - ratio++; - new PlaceObject2Tag(swf, false, false, false, true, false, false, false, true, 1, 0, null, null, ratio, null, 0, null).writeTag(sos2); - } - f.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - first = false; - } - } else if (tagObj instanceof DefineSpriteTag) { - DefineSpriteTag s = (DefineSpriteTag) tagObj; - Tag lastTag = null; - for (Tag t : s.subTags) { - if (t instanceof EndTag) { - break; - } else if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t; - MATRIX m = pt.getMatrix(); - MATRIX m2 = new Matrix(m).preConcatenate(new Matrix(mat)).toMATRIX(); - pt.writeTagWithMatrix(sos2, m2); - lastTag = t; - } else { - t.writeTag(sos2); - lastTag = t; - } - } - if (!s.subTags.isEmpty() && (lastTag != null) && (!(lastTag instanceof ShowFrameTag))) { - new ShowFrameTag(swf).writeTag(sos2); - } - } else { - new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - - } // not showframe - - new EndTag(swf).writeTag(sos2); - data = baos.toByteArray(); - } - - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { - SWFOutputStream sos = new SWFOutputStream(fos, Math.max(10, swf.version)); - sos.write("FWS".getBytes()); - sos.write(swf.version); - sos.writeUI32(sos.getPos() + data.length + 4); - sos.write(data); - fos.flush(); - } - if (flashPanel != null) { - flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, frameRate); - } - showFlashViewerPanel(); - } catch (IOException | ActionParseException ex) { - Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, null, ex); - } - } - - public void showSwf(SWF swf) { - Color backgroundColor = View.getDefaultBackgroundColor(); - for (Tag t : swf.tags) { - if (t instanceof SetBackgroundColorTag) { - backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); - break; - } - } - - if (tempFile != null) { - tempFile.delete(); - } - try { - tempFile = File.createTempFile("ffdec_view_", ".swf"); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { - swf.saveTo(fos); - } - flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, swf.frameRate); - } catch (IOException iex) { - Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, "Cannot create tempfile", iex); - } - } - - private void editMetadataButtonActionPerformed(ActionEvent evt) { - TreeItem item = mainPanel.tagTree.getCurrentTreeItem(); - if (item == null) { - return; - } - - if (item instanceof MetadataTag) { - metadataEditor.setEditable(true); - updateMetadataButtonsVisibility(); - } - } - - private void saveMetadataButtonActionPerformed(ActionEvent evt) { - metadataTag.xmlMetadata = metadataEditor.getText().replaceAll(">\r?\n<", "> <"); - metadataTag.setModified(true); - metadataEditor.setEditable(Configuration.editorMode.get()); - setMetadataModified(false); - updateMetadataButtonsVisibility(); - mainPanel.repaintTree(); - } - - private void cancelMetadataButtonActionPerformed(ActionEvent evt) { - metadataEditor.setEditable(false); - metadataEditor.setText(formatMetadata(metadataTag.xmlMetadata, 4)); - metadataEditor.setEditable(Configuration.editorMode.get()); - setMetadataModified(false); - updateMetadataButtonsVisibility(); - } - - private void editGenericTagButtonActionPerformed(ActionEvent evt) { - TreeItem item = mainPanel.tagTree.getCurrentTreeItem(); - if (item == null) { - return; - } - - if (item instanceof TagScript) { - item = ((TagScript) item).getTag(); - } - - if (item instanceof Tag) { - genericEditButton.setVisible(false); - genericSaveButton.setVisible(true); - genericCancelButton.setVisible(true); - genericTagPanel.setEditMode(true, (Tag) item); - } - } - - private void saveGenericTagButtonActionPerformed(ActionEvent evt) { - genericTagPanel.save(); - Tag tag = genericTagPanel.getTag(); - SWF swf = tag.getSwf(); - swf.clearImageCache(); - swf.updateCharacters(); - tag.getTimelined().resetTimeline(); - swf.assignClassesToSymbols(); - swf.assignExportNamesToSymbols(); - mainPanel.repaintTree(); - mainPanel.setTagTreeSelectedNode(tag); - genericEditButton.setVisible(true); - genericSaveButton.setVisible(false); - genericCancelButton.setVisible(false); - genericTagPanel.setEditMode(false, null); - } - - private void cancelGenericTagButtonActionPerformed(ActionEvent evt) { - genericEditButton.setVisible(true); - genericSaveButton.setVisible(false); - genericCancelButton.setVisible(false); - genericTagPanel.setEditMode(false, null); - } - - private void prevFontsButtonActionPerformed(ActionEvent evt) { - FontTag fontTag = fontPanel.getFontTag(); - int pageCount = getFontPageCount(fontTag); - fontPageNum = (fontPageNum + pageCount - 1) % pageCount; - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - imagePanel.setTimelined(MainPanel.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0); - } - } - - private void nextFontsButtonActionPerformed(ActionEvent evt) { - FontTag fontTag = fontPanel.getFontTag(); - int pageCount = getFontPageCount(fontTag); - fontPageNum = (fontPageNum + 1) % pageCount; - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - imagePanel.setTimelined(MainPanel.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0); - } - } - - @Override - public boolean tryAutoSave() { - // todo: implement - return textPanel.tryAutoSave() && false; - } - - @Override - public boolean isEditing() { - return textPanel.isEditing() - || (genericSaveButton.isVisible() && genericSaveButton.isEnabled()) - || (metadataSaveButton.isVisible() && metadataSaveButton.isEnabled()); - } -} +/* + * Copyright (C) 2010-2015 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.SWFOutputStream; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.parser.ActionParseException; +import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; +import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; +import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; +import com.jpexs.decompiler.flash.gui.player.MediaDisplay; +import com.jpexs.decompiler.flash.gui.player.PlayerControls; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineBitsTag; +import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DefineTextTag; +import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; +import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import com.jpexs.decompiler.flash.tags.EndTag; +import com.jpexs.decompiler.flash.tags.ExportAssetsTag; +import com.jpexs.decompiler.flash.tags.JPEGTablesTag; +import com.jpexs.decompiler.flash.tags.MetadataTag; +import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.VideoFrameTag; +import com.jpexs.decompiler.flash.tags.base.AloneTag; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.timeline.TagScript; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.flash.types.GLYPHENTRY; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.TEXTRECORD; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.SerializableImage; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.SwingConstants; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +/** + * + * @author JPEXS + */ +public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel { + + private static final String FLASH_VIEWER_CARD = "FLASHVIEWER"; + + private static final String DRAW_PREVIEW_CARD = "DRAWPREVIEW"; + + private static final String GENERIC_TAG_CARD = "GENERICTAG"; + + private static final String BINARY_TAG_CARD = "BINARYTAG"; + + private static final String METADATA_TAG_CARD = "METADATATAG"; + + private static final String CARDTEXTPANEL = "Text card"; + + private static final String CARDFONTPANEL = "Font card"; + + private final MainPanel mainPanel; + + private final JPanel viewerCards; + + private final FlashPlayerPanel flashPanel; + + private File tempFile; + + private ImagePanel imagePanel; + + private PlayerControls imagePlayControls; + + private MediaDisplay media; + + private BinaryPanel binaryPanel; + + private LineMarkedEditorPane metadataEditor; + + private GenericTagPanel genericTagPanel; + + private JPanel displayWithPreview; + + // Image tag buttons + private JButton replaceImageButton; + + private JButton replaceImageAlphaButton; + + private JButton prevFontsButton; + + private JButton nextFontsButton; + + // Binary tag buttons + private JButton replaceBinaryButton; + + // Metadata editor buttons + private JButton metadataEditButton; + + private JButton metadataSaveButton; + + private JButton metadataCancelButton; + + // Generic tag buttons + private JButton genericEditButton; + + private JButton genericSaveButton; + + private JButton genericCancelButton; + + private JPanel parametersPanel; + + private FontPanel fontPanel; + + private int fontPageNum; + + private TextPanel textPanel; + + private MetadataTag metadataTag; + + public PreviewPanel(MainPanel mainPanel, FlashPlayerPanel flashPanel) { + super(JSplitPane.HORIZONTAL_SPLIT, Configuration.guiPreviewSplitPaneDividerLocationPercent); + this.mainPanel = mainPanel; + this.flashPanel = flashPanel; + + viewerCards = new JPanel(); + viewerCards.setLayout(new CardLayout()); + + viewerCards.add(createFlashPlayerPanel(flashPanel), FLASH_VIEWER_CARD); + viewerCards.add(createImagesCard(), DRAW_PREVIEW_CARD); + viewerCards.add(createBinaryCard(), BINARY_TAG_CARD); + viewerCards.add(createMetadataCard(), METADATA_TAG_CARD); + viewerCards.add(createGenericTagCard(), GENERIC_TAG_CARD); + setLeftComponent(viewerCards); + + createParametersPanel(); + + showCardLeft(FLASH_VIEWER_CARD); + } + + private void createParametersPanel() { + displayWithPreview = new JPanel(new CardLayout()); + + textPanel = new TextPanel(mainPanel); + displayWithPreview.add(textPanel, CARDTEXTPANEL); + + fontPanel = new FontPanel(mainPanel); + displayWithPreview.add(fontPanel, CARDFONTPANEL); + + JLabel paramsLabel = new HeaderLabel(mainPanel.translate("parameters")); + paramsLabel.setHorizontalAlignment(SwingConstants.CENTER); + //paramsLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + + parametersPanel = new JPanel(new BorderLayout()); + parametersPanel.add(paramsLabel, BorderLayout.NORTH); + parametersPanel.add(displayWithPreview, BorderLayout.CENTER); + setRightComponent(parametersPanel); + } + + private JPanel createImageButtonsPanel() { + replaceImageButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("replaceimage16")); + replaceImageButton.setMargin(new Insets(3, 3, 3, 10)); + replaceImageButton.addActionListener(mainPanel::replaceButtonActionPerformed); + replaceImageButton.setVisible(false); + + replaceImageAlphaButton = new JButton(mainPanel.translate("button.replaceAlphaChannel"), View.getIcon("replacealpha16")); + replaceImageAlphaButton.setMargin(new Insets(3, 3, 3, 10)); + replaceImageAlphaButton.addActionListener(mainPanel::replaceAlphaButtonActionPerformed); + replaceImageAlphaButton.setVisible(false); + + prevFontsButton = new JButton(mainPanel.translate("button.prev"), View.getIcon("prev16")); + prevFontsButton.setMargin(new Insets(3, 3, 3, 10)); + prevFontsButton.addActionListener(this::prevFontsButtonActionPerformed); + prevFontsButton.setVisible(false); + + nextFontsButton = new JButton(mainPanel.translate("button.next"), View.getIcon("next16")); + nextFontsButton.setMargin(new Insets(3, 3, 3, 10)); + nextFontsButton.addActionListener(this::nextFontsButtonActionPerformed); + nextFontsButton.setVisible(false); + + ButtonsPanel imageButtonsPanel = new ButtonsPanel(); + imageButtonsPanel.add(replaceImageButton); + imageButtonsPanel.add(replaceImageAlphaButton); + imageButtonsPanel.add(prevFontsButton); + imageButtonsPanel.add(nextFontsButton); + return imageButtonsPanel; + } + + private JPanel createBinaryButtonsPanel() { + replaceBinaryButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("edit16")); + replaceBinaryButton.setMargin(new Insets(3, 3, 3, 10)); + replaceBinaryButton.addActionListener(mainPanel::replaceButtonActionPerformed); + + ButtonsPanel binaryButtonsPanel = new ButtonsPanel(); + binaryButtonsPanel.add(replaceBinaryButton); + return binaryButtonsPanel; + } + + private JPanel createGenericTagButtonsPanel() { + genericEditButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16")); + genericEditButton.setMargin(new Insets(3, 3, 3, 10)); + genericEditButton.addActionListener(this::editGenericTagButtonActionPerformed); + genericSaveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); + genericSaveButton.setMargin(new Insets(3, 3, 3, 10)); + genericSaveButton.addActionListener(this::saveGenericTagButtonActionPerformed); + genericSaveButton.setVisible(false); + genericCancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); + genericCancelButton.setMargin(new Insets(3, 3, 3, 10)); + genericCancelButton.addActionListener(this::cancelGenericTagButtonActionPerformed); + genericCancelButton.setVisible(false); + + ButtonsPanel genericTagButtonsPanel = new ButtonsPanel(); + genericTagButtonsPanel.add(genericEditButton); + genericTagButtonsPanel.add(genericSaveButton); + genericTagButtonsPanel.add(genericCancelButton); + return genericTagButtonsPanel; + } + + private JPanel createMetadataButtonsPanel() { + metadataEditButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16")); + metadataEditButton.setMargin(new Insets(3, 3, 3, 10)); + metadataEditButton.addActionListener(this::editMetadataButtonActionPerformed); + metadataSaveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); + metadataSaveButton.setMargin(new Insets(3, 3, 3, 10)); + metadataSaveButton.addActionListener(this::saveMetadataButtonActionPerformed); + metadataSaveButton.setVisible(false); + metadataCancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); + metadataCancelButton.setMargin(new Insets(3, 3, 3, 10)); + metadataCancelButton.addActionListener(this::cancelMetadataButtonActionPerformed); + metadataCancelButton.setVisible(false); + + ButtonsPanel metadataTagButtonsPanel = new ButtonsPanel(); + metadataTagButtonsPanel.add(metadataEditButton); + metadataTagButtonsPanel.add(metadataSaveButton); + metadataTagButtonsPanel.add(metadataCancelButton); + return metadataTagButtonsPanel; + } + + private JPanel createFlashPlayerPanel(FlashPlayerPanel flashPanel) { + JPanel pan = new JPanel(new BorderLayout()); + JLabel prevLabel = new HeaderLabel(mainPanel.translate("swfpreview")); + prevLabel.setHorizontalAlignment(SwingConstants.CENTER); + //prevLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + + pan.add(prevLabel, BorderLayout.NORTH); + + Component leftComponent; + if (flashPanel != null) { + JPanel flashPlayPanel = new JPanel(new BorderLayout()); + flashPlayPanel.add(flashPanel, BorderLayout.CENTER); + + /*JPanel bottomPanel = new JPanel(new BorderLayout()); + JPanel buttonsPanel = new JPanel(new FlowLayout()); + JButton selectColorButton = new JButton(View.getIcon("color16")); + selectColorButton.addActionListener(mainPanel::selectBkColor); + selectColorButton.setToolTipText(AppStrings.translate("button.selectbkcolor.hint")); + buttonsPanel.add(selectColorButton); + bottomPanel.add(buttonsPanel, BorderLayout.EAST); + + flashPlayPanel.add(bottomPanel, BorderLayout.SOUTH);*/ + JPanel flashPlayPanel2 = new JPanel(new BorderLayout()); + flashPlayPanel2.add(flashPlayPanel, BorderLayout.CENTER); + flashPlayPanel2.add(new PlayerControls(mainPanel, flashPanel), BorderLayout.SOUTH); + leftComponent = flashPlayPanel2; + } else { + JPanel swtPanel = new JPanel(new BorderLayout()); + swtPanel.add(new JLabel("
" + mainPanel.translate("notavailonthisplatform") + "
", JLabel.CENTER), BorderLayout.CENTER); + swtPanel.setBackground(View.getDefaultBackgroundColor()); + leftComponent = swtPanel; + } + + pan.add(leftComponent, BorderLayout.CENTER); + return pan; + } + + private JPanel createImagesCard() { + JPanel shapesCard = new JPanel(new BorderLayout()); + JPanel previewPanel = new JPanel(new BorderLayout()); + + JPanel previewCnt = new JPanel(new BorderLayout()); + imagePanel = new ImagePanel(); + imagePanel.setLoop(Configuration.loopMedia.get()); + previewCnt.add(imagePanel, BorderLayout.CENTER); + previewCnt.add(imagePlayControls = new PlayerControls(mainPanel, imagePanel), BorderLayout.SOUTH); + imagePlayControls.setMedia(imagePanel); + previewPanel.add(previewCnt, BorderLayout.CENTER); + JLabel prevIntLabel = new HeaderLabel(mainPanel.translate("swfpreview.internal")); + prevIntLabel.setHorizontalAlignment(SwingConstants.CENTER); + //prevIntLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + previewPanel.add(prevIntLabel, BorderLayout.NORTH); + + shapesCard.add(previewPanel, BorderLayout.CENTER); + + shapesCard.add(createImageButtonsPanel(), BorderLayout.SOUTH); + return shapesCard; + } + + private JPanel createMetadataCard() { + JPanel metadataCard = new JPanel(new BorderLayout()); + metadataEditor = new LineMarkedEditorPane(); + metadataCard.add(new JScrollPane(metadataEditor), BorderLayout.CENTER); + //metadataEditor.setContentType("text/xml"); + metadataEditor.setEditable(false); + + metadataEditor.setFont(new Font("Monospaced", Font.PLAIN, metadataEditor.getFont().getSize())); + metadataEditor.changeContentType("text/xml"); + metadataEditor.addTextChangedListener(this::metadataTextChanged); + + metadataCard.add(createMetadataButtonsPanel(), BorderLayout.SOUTH); + return metadataCard; + } + + private boolean isMetadataModified() { + return metadataSaveButton.isVisible() && metadataSaveButton.isEnabled(); + } + + private void setMetadataModified(boolean value) { + metadataSaveButton.setEnabled(value); + metadataCancelButton.setEnabled(value); + } + + private void metadataTextChanged() { + setMetadataModified(true); + } + + private void updateMetadataButtonsVisibility() { + boolean edit = metadataEditor.isEditable(); + boolean editorMode = Configuration.editorMode.get(); + metadataEditButton.setVisible(!edit); + metadataSaveButton.setVisible(edit); + boolean metadataModified = isMetadataModified(); + metadataCancelButton.setVisible(edit); + metadataCancelButton.setEnabled(metadataModified || !editorMode); + } + + private JPanel createBinaryCard() { + JPanel binaryCard = new JPanel(new BorderLayout()); + binaryPanel = new BinaryPanel(mainPanel); + binaryCard.add(binaryPanel, BorderLayout.CENTER); + binaryCard.add(createBinaryButtonsPanel(), BorderLayout.SOUTH); + return binaryCard; + } + + private JPanel createGenericTagCard() { + JPanel genericTagCard = new JPanel(new BorderLayout()); + genericTagPanel = new GenericTagTreePanel(mainPanel); + genericTagCard.add(genericTagPanel, BorderLayout.CENTER); + genericTagCard.add(createGenericTagButtonsPanel(), BorderLayout.SOUTH); + return genericTagCard; + } + + private void showCardLeft(String card) { + CardLayout cl = (CardLayout) (viewerCards.getLayout()); + cl.show(viewerCards, card); + } + + private void showCardRight(String card) { + CardLayout cl = (CardLayout) (displayWithPreview.getLayout()); + cl.show(displayWithPreview, card); + } + + public TextPanel getTextPanel() { + return textPanel; + } + + public void setParametersPanelVisible(boolean show) { + parametersPanel.setVisible(show); + } + + public void showFlashViewerPanel() { + parametersPanel.setVisible(false); + showCardLeft(FLASH_VIEWER_CARD); + } + + public void showImagePanel(Timelined timelined, SWF swf, int frame) { + showCardLeft(DRAW_PREVIEW_CARD); + parametersPanel.setVisible(false); + imagePlayControls.setMedia(imagePanel); + imagePanel.setTimelined(timelined, swf, frame); + } + + public void showImagePanel(SerializableImage image) { + showCardLeft(DRAW_PREVIEW_CARD); + parametersPanel.setVisible(false); + imagePlayControls.setMedia(imagePanel); + imagePanel.setImage(image); + } + + public void showTextComparePanel(TextTag textTag, TextTag newTextTag) { + imagePanel.setText(textTag, newTextTag); + } + + public void setMedia(MediaDisplay media) { + this.media = media; + imagePlayControls.setMedia(media); + } + + public void showFontPanel(FontTag fontTag) { + fontPageNum = 0; + showFontPage(fontTag); + + showCardRight(CARDFONTPANEL); + parametersPanel.setVisible(true); + fontPanel.showFontTag(fontTag); + + int pageCount = getFontPageCount(fontTag); + if (pageCount > 1) { + prevFontsButton.setVisible(true); + nextFontsButton.setVisible(true); + } + } + + private void showFontPage(FontTag fontTag) { + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + showImagePanel(MainPanel.makeTimelined(fontTag), fontTag.getSwf(), fontPageNum); + } + } + + public static int getFontPageCount(FontTag fontTag) { + int pageCount = (fontTag.getGlyphShapeTable().size() - 1) / SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW + 1; + if (pageCount < 1) { + pageCount = 1; + } + return pageCount; + } + + public void showTextPanel(TextTag textTag) { + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + showImagePanel(MainPanel.makeTimelined(textTag), textTag.getSwf(), 0); + } + + showCardRight(CARDTEXTPANEL); + parametersPanel.setVisible(true); + textPanel.setText(textTag); + } + + public void focusTextPanel() { + textPanel.focusTextValue(); + } + + public void clear() { + imagePanel.clearAll(); + if (media != null) { + try { + media.close(); + } catch (IOException ex) { + // ignore + } + } + + binaryPanel.setBinaryData(null); + genericTagPanel.clear(); + fontPanel.clear(); + } + + public void closeTag() { + textPanel.closeTag(); + } + + public static String formatMetadata(String input, int indent) { + input = input.replace("> <", "><"); + try { + Source xmlInput = new StreamSource(new StringReader(input)); + StringWriter stringWriter = new StringWriter(); + StreamResult xmlOutput = new StreamResult(stringWriter); + StringWriter sw = new StringWriter(); + xmlOutput.setWriter(sw); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + transformerFactory.setAttribute("indent-number", indent); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "" + indent); + transformer.transform(xmlInput, xmlOutput); + + return xmlOutput.getWriter().toString(); + } catch (IllegalArgumentException | TransformerException e) { + return input; + } + } + + public void showMetaDataPanel(MetadataTag metadataTag) { + showCardLeft(METADATA_TAG_CARD); + this.metadataTag = metadataTag; + metadataEditor.setEditable(Configuration.editorMode.get()); + metadataEditor.setText(formatMetadata(metadataTag.xmlMetadata, 4)); + setMetadataModified(false); + updateMetadataButtonsVisibility(); + parametersPanel.setVisible(false); + } + + public void showBinaryPanel(DefineBinaryDataTag binaryDataTag) { + showCardLeft(BINARY_TAG_CARD); + binaryPanel.setBinaryData(binaryDataTag); + parametersPanel.setVisible(false); + } + + public void showGenericTagPanel(Tag tag) { + showCardLeft(GENERIC_TAG_CARD); + genericEditButton.setVisible(true); + genericSaveButton.setVisible(false); + genericCancelButton.setVisible(false); + genericTagPanel.setEditMode(false, tag); + parametersPanel.setVisible(false); + } + + public void setImageReplaceButtonVisible(boolean show, boolean showAlpha) { + replaceImageButton.setVisible(show); + replaceImageAlphaButton.setVisible(showAlpha); + prevFontsButton.setVisible(false); + nextFontsButton.setVisible(false); + } + + private static Tag classicTag(Tag t) { + if (t instanceof DefineCompactedFont) { + return ((DefineCompactedFont) t).toClassicFont(); + } + return t; + } + + public void createAndShowTempSwf(TreeItem tagObj) { + SWF swf; + try { + if (tempFile != null) { + tempFile.delete(); + } + + tempFile = File.createTempFile("ffdec_view_", ".swf"); + tempFile.deleteOnExit(); + + Color backgroundColor = View.getSwfBackgroundColor(); + + if (tagObj instanceof FontTag) { //Fonts are always black on white + backgroundColor = View.getDefaultBackgroundColor(); + } + + if (tagObj instanceof Frame) { + Frame fn = (Frame) tagObj; + swf = fn.getSwf(); + if (fn.timeline.timelined == swf) { + for (Tag t : swf.tags) { + if (t instanceof SetBackgroundColorTag) { + backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); + break; + } + } + } + } else { + Tag tag = (Tag) tagObj; + swf = tag.getSwf(); + } + + int frameCount = 1; + int frameRate = swf.frameRate; + HashMap videoFrames = new HashMap<>(); + if (tagObj instanceof DefineVideoStreamTag) { + DefineVideoStreamTag vs = (DefineVideoStreamTag) tagObj; + SWF.populateVideoFrames(vs.getCharacterId(), swf.tags, videoFrames); + frameCount = videoFrames.size(); + } + + List soundFrames = new ArrayList<>(); + if (tagObj instanceof SoundStreamHeadTypeTag) { + soundFrames = ((SoundStreamHeadTypeTag) tagObj).getBlocks(); + frameCount = soundFrames.size(); + } + + if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { + frameRate = MainPanel.MORPH_SHAPE_ANIMATION_FRAME_RATE; + frameCount = MainPanel.MORPH_SHAPE_ANIMATION_LENGTH * frameRate; + } + + if (tagObj instanceof DefineSoundTag) { + frameCount = 1; + } + + if (tagObj instanceof DefineSpriteTag) { + frameCount = ((DefineSpriteTag) tagObj).frameCount; + } + + byte[] data; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + SWFOutputStream sos2 = new SWFOutputStream(baos, SWF.DEFAULT_VERSION); + RECT outrect = new RECT(swf.displayRect); + + if (tagObj instanceof FontTag) { + outrect.Xmin = 0; + outrect.Ymin = 0; + outrect.Xmax = FontTag.PREVIEWSIZE * 20; + outrect.Ymax = FontTag.PREVIEWSIZE * 20; + } + int width = outrect.getWidth(); + int height = outrect.getHeight(); + + sos2.writeRECT(outrect); + sos2.writeUI8(0); + sos2.writeUI8(frameRate); + sos2.writeUI16(frameCount); //framecnt + + /*FileAttributesTag fa = new FileAttributesTag(); + sos2.writeTag(fa); + */ + new SetBackgroundColorTag(swf, new RGB(backgroundColor)).writeTag(sos2); + + if (tagObj instanceof Frame) { + Frame fn = (Frame) tagObj; + Timelined parent = fn.timeline.timelined; + List subs = fn.timeline.tags; + List doneCharacters = new ArrayList<>(); + int frameCnt = 0; + for (Tag t : subs) { + if (t instanceof ShowFrameTag) { + frameCnt++; + continue; + } + if (frameCnt > fn.frame) { + break; + } + + if (t instanceof DoActionTag || t instanceof DoInitActionTag) { + // todo: Maybe DoABC tags should be removed, too + continue; + } + + Set needed = new HashSet<>(); + t.getNeededCharactersDeep(needed); + for (int n : needed) { + if (!doneCharacters.contains(n)) { + classicTag(swf.getCharacter(n)).writeTag(sos2); + doneCharacters.add(n); + } + } + if (t instanceof CharacterTag) { + int characterId = ((CharacterTag) t).getCharacterId(); + if (!doneCharacters.contains(characterId)) { + doneCharacters.add(((CharacterTag) t).getCharacterId()); + } + } + classicTag(t).writeTag(sos2); + + if (parent != null) { + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag pot = (PlaceObjectTypeTag) t; + int chid = pot.getCharacterId(); + int depth = pot.getDepth(); + MATRIX mat = pot.getMatrix(); + if (mat == null) { + mat = new MATRIX(); + } + mat = Helper.deepCopy(mat); + if (parent instanceof BoundedTag) { + RECT r = ((BoundedTag) parent).getRect(); + mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; + mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; + } else { + mat.translateX += width / 2; + mat.translateY += height / 2; + } + new PlaceObject2Tag(swf, false, false, false, false, false, true, false, true, depth, chid, mat, null, 0, null, 0, null).writeTag(sos2); + + } + } + } + new ShowFrameTag(swf).writeTag(sos2); + } else { + + boolean isSprite = false; + if (tagObj instanceof DefineSpriteTag) { + isSprite = true; + } + int chtId = 0; + if (tagObj instanceof CharacterTag) { + chtId = ((CharacterTag) tagObj).getCharacterId(); + } + + if (tagObj instanceof DefineBitsTag) { + JPEGTablesTag jtt = swf.getJtt(); + if (jtt != null) { + jtt.writeTag(sos2); + } + } else if (tagObj instanceof AloneTag) { + } else { + Set needed = new HashSet<>(); + ((Tag) tagObj).getNeededCharactersDeep(needed); + for (int n : needed) { + if (isSprite && chtId == n) { + continue; + } + + CharacterTag characterTag = swf.getCharacter(n); + if (characterTag instanceof DefineBitsTag) { + JPEGTablesTag jtt = swf.getJtt(); + if (jtt != null) { + jtt.writeTag(sos2); + } + } + + classicTag(characterTag).writeTag(sos2); + } + } + + classicTag((Tag) tagObj).writeTag(sos2); + + MATRIX mat = new MATRIX(); + mat.hasRotate = false; + mat.hasScale = false; + mat.translateX = 0; + mat.translateY = 0; + if (tagObj instanceof BoundedTag) { + RECT r = ((BoundedTag) tagObj).getRect(); + mat.translateX = -r.Xmin; + mat.translateY = -r.Ymin; + mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; + mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; + } else { + mat.translateX = width / 4; + mat.translateY = height / 4; + } + if (tagObj instanceof FontTag) { + + FontTag ft = (FontTag) classicTag((Tag) tagObj); + + int countGlyphsTotal = ft.getGlyphShapeTable().size(); + int countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal); + int fontId = ft.getFontId(); + int cols = (int) Math.ceil(Math.sqrt(countGlyphs)); + int rows = (int) Math.ceil(((float) countGlyphs) / ((float) cols)); + if (rows == 0) { + rows = 1; + cols = 1; + } + int x = 0; + int y = 0; + int firstGlyphIndex = fontPageNum * SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW; + countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal - firstGlyphIndex); + List shapes = ft.getGlyphShapeTable(); + int maxw = 0; + for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { + RECT b = shapes.get(f).getBounds(); + if (b.Xmin == Integer.MAX_VALUE) { + continue; + } + if (b.Ymin == Integer.MAX_VALUE) { + continue; + } + int w = (int) (b.getWidth() / ft.getDivider()); + if (w > maxw) { + maxw = w; + } + x++; + } + + x = 0; + + int BORDER = 3 * 20; + + int textHeight = height / rows; + + while (maxw * textHeight / 1024.0 > width / cols - 2 * BORDER) { + textHeight--; + } + + MATRIX tmat = new MATRIX(); + for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { + if (x >= cols) { + x = 0; + y++; + } + List rec = new ArrayList<>(); + TEXTRECORD tr = new TEXTRECORD(); + + RECT b = shapes.get(f).getBounds(); + int xmin = b.Xmin == Integer.MAX_VALUE ? 0 : (int) (b.Xmin / ft.getDivider()); + xmin *= textHeight / 1024.0; + int ymin = b.Ymin == Integer.MAX_VALUE ? 0 : (int) (b.Ymin / ft.getDivider()); + ymin *= textHeight / 1024.0; + int w = (int) (b.getWidth() / ft.getDivider()); + w *= textHeight / 1024.0; + int h = (int) (b.getHeight() / ft.getDivider()); + h *= textHeight / 1024.0; + + tr.fontId = fontId; + tr.styleFlagsHasFont = true; + tr.textHeight = textHeight; + tr.xOffset = -xmin; + tr.yOffset = 0; + tr.styleFlagsHasXOffset = true; + tr.styleFlagsHasYOffset = true; + tr.glyphEntries = new ArrayList<>(1); + tr.styleFlagsHasColor = true; + tr.textColor = new RGB(0, 0, 0); + GLYPHENTRY ge = new GLYPHENTRY(); + + double ga = ft.getGlyphAdvance(f); + int cw = ga == -1 ? w : (int) (ga / ft.getDivider() * textHeight / 1024.0); + + ge.glyphAdvance = 0; + ge.glyphIndex = f; + tr.glyphEntries.add(ge); + rec.add(tr); + + tmat.translateX = x * width / cols + width / cols / 2 - w / 2; + tmat.translateY = y * height / rows + height / rows / 2; + new DefineTextTag(swf, 999 + f, new RECT(0, cw, ymin, ymin + h), new MATRIX(), rec).writeTag(sos2); + new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1 + f, 999 + f, tmat, null, 0, null, 0, null).writeTag(sos2); + x++; + } + new ShowFrameTag(swf).writeTag(sos2); + } else if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { + new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + for (int ratio = 0; ratio < 65536; ratio += 65536 / frameCount) { + new PlaceObject2Tag(swf, false, false, false, true, false, true, false, true, 1, chtId, mat, null, ratio, null, 0, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + } else if (tagObj instanceof SoundStreamHeadTypeTag) { + for (SoundStreamBlockTag blk : soundFrames) { + blk.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + } else if (tagObj instanceof DefineSoundTag) { + ExportAssetsTag ea = new ExportAssetsTag(swf); + DefineSoundTag ds = (DefineSoundTag) tagObj; + ea.tags.add(ds.soundId); + ea.names.add("my_define_sound"); + ea.writeTag(sos2); + List actions; + DoActionTag doa; + + doa = new DoActionTag(swf, null); + actions = ASMParser.parse(0, false, + "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\"\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\" 0.0 \"Sound\"\n" + + "NewObject\n" + + "SetMember\n" + + "Push \"my_define_sound\" 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"attachSound\"\n" + + "CallMethod\n" + + "Pop\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + actions = ASMParser.parse(0, false, + "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"start\"\n" + + "StopSounds\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\" 0.0 \"Sound\"\n" + + "NewObject\n" + + "SetMember\n" + + "Push \"my_define_sound\" 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"attachSound\"\n" + + "CallMethod\n" + + "Pop\n" + + "Push 9999 0.0 2 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"start\"\n" + + "CallMethod\n" + + "Pop\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + actions = ASMParser.parse(0, false, + "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"onSoundComplete\" \"start\" \"execParam\"\n" + + "StopSounds\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\" 0.0 \"Sound\"\n" + + "NewObject\n" + + "SetMember\n" + + "Push \"my_define_sound\" 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"attachSound\"\n" + + "CallMethod\n" + + "Pop\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"onSoundComplete\"\n" + + "DefineFunction2 \"\" 0 2 false true true false true false true false false {\n" + + "Push 0.0 register1 \"my_sound\"\n" + + "GetMember\n" + + "Push \"start\"\n" + + "CallMethod\n" + + "Pop\n" + + "}\n" + + "SetMember\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"execParam\"\n" + + "GetMember\n" + + "Push 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"start\"\n" + + "CallMethod\n" + + "Pop\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + actions = ASMParser.parse(0, false, + "StopSounds\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + new ShowFrameTag(swf).writeTag(sos2); + } else if (tagObj instanceof DefineVideoStreamTag) { + + new PlaceObject2Tag(swf, false, false, false, false, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); + List frs = new ArrayList<>(videoFrames.values()); + Collections.sort(frs, new Comparator() { + @Override + public int compare(VideoFrameTag o1, VideoFrameTag o2) { + return o1.frameNum - o2.frameNum; + } + }); + boolean first = true; + int ratio = 0; + for (VideoFrameTag f : frs) { + if (!first) { + ratio++; + new PlaceObject2Tag(swf, false, false, false, true, false, false, false, true, 1, 0, null, null, ratio, null, 0, null).writeTag(sos2); + } + f.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + first = false; + } + } else if (tagObj instanceof DefineSpriteTag) { + DefineSpriteTag s = (DefineSpriteTag) tagObj; + Tag lastTag = null; + for (Tag t : s.subTags) { + if (t instanceof EndTag) { + break; + } else if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t; + MATRIX m = pt.getMatrix(); + MATRIX m2 = new Matrix(m).preConcatenate(new Matrix(mat)).toMATRIX(); + pt.writeTagWithMatrix(sos2, m2); + lastTag = t; + } else { + t.writeTag(sos2); + lastTag = t; + } + } + if (!s.subTags.isEmpty() && (lastTag != null) && (!(lastTag instanceof ShowFrameTag))) { + new ShowFrameTag(swf).writeTag(sos2); + } + } else { + new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + + } // not showframe + + new EndTag(swf).writeTag(sos2); + data = baos.toByteArray(); + } + + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { + SWFOutputStream sos = new SWFOutputStream(fos, Math.max(10, swf.version)); + sos.write("FWS".getBytes()); + sos.write(swf.version); + sos.writeUI32(sos.getPos() + data.length + 4); + sos.write(data); + fos.flush(); + } + if (flashPanel != null) { + flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, frameRate); + } + showFlashViewerPanel(); + } catch (IOException | ActionParseException ex) { + Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public void showSwf(SWF swf) { + Color backgroundColor = View.getDefaultBackgroundColor(); + for (Tag t : swf.tags) { + if (t instanceof SetBackgroundColorTag) { + backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); + break; + } + } + + if (tempFile != null) { + tempFile.delete(); + } + try { + tempFile = File.createTempFile("ffdec_view_", ".swf"); + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { + swf.saveTo(fos); + } + flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, swf.frameRate); + } catch (IOException iex) { + Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, "Cannot create tempfile", iex); + } + } + + private void editMetadataButtonActionPerformed(ActionEvent evt) { + TreeItem item = mainPanel.tagTree.getCurrentTreeItem(); + if (item == null) { + return; + } + + if (item instanceof MetadataTag) { + metadataEditor.setEditable(true); + updateMetadataButtonsVisibility(); + } + } + + private void saveMetadataButtonActionPerformed(ActionEvent evt) { + metadataTag.xmlMetadata = metadataEditor.getText().replaceAll(">\r?\n<", "> <"); + metadataTag.setModified(true); + metadataEditor.setEditable(Configuration.editorMode.get()); + setMetadataModified(false); + updateMetadataButtonsVisibility(); + mainPanel.repaintTree(); + } + + private void cancelMetadataButtonActionPerformed(ActionEvent evt) { + metadataEditor.setEditable(false); + metadataEditor.setText(formatMetadata(metadataTag.xmlMetadata, 4)); + metadataEditor.setEditable(Configuration.editorMode.get()); + setMetadataModified(false); + updateMetadataButtonsVisibility(); + } + + private void editGenericTagButtonActionPerformed(ActionEvent evt) { + TreeItem item = mainPanel.tagTree.getCurrentTreeItem(); + if (item == null) { + return; + } + + if (item instanceof TagScript) { + item = ((TagScript) item).getTag(); + } + + if (item instanceof Tag) { + genericEditButton.setVisible(false); + genericSaveButton.setVisible(true); + genericCancelButton.setVisible(true); + genericTagPanel.setEditMode(true, (Tag) item); + } + } + + private void saveGenericTagButtonActionPerformed(ActionEvent evt) { + genericTagPanel.save(); + Tag tag = genericTagPanel.getTag(); + SWF swf = tag.getSwf(); + swf.clearImageCache(); + swf.updateCharacters(); + tag.getTimelined().resetTimeline(); + swf.assignClassesToSymbols(); + swf.assignExportNamesToSymbols(); + mainPanel.repaintTree(); + mainPanel.setTagTreeSelectedNode(tag); + genericEditButton.setVisible(true); + genericSaveButton.setVisible(false); + genericCancelButton.setVisible(false); + genericTagPanel.setEditMode(false, null); + } + + private void cancelGenericTagButtonActionPerformed(ActionEvent evt) { + genericEditButton.setVisible(true); + genericSaveButton.setVisible(false); + genericCancelButton.setVisible(false); + genericTagPanel.setEditMode(false, null); + } + + private void prevFontsButtonActionPerformed(ActionEvent evt) { + FontTag fontTag = fontPanel.getFontTag(); + int pageCount = getFontPageCount(fontTag); + fontPageNum = (fontPageNum + pageCount - 1) % pageCount; + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + imagePanel.setTimelined(MainPanel.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0); + } + } + + private void nextFontsButtonActionPerformed(ActionEvent evt) { + FontTag fontTag = fontPanel.getFontTag(); + int pageCount = getFontPageCount(fontTag); + fontPageNum = (fontPageNum + 1) % pageCount; + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + imagePanel.setTimelined(MainPanel.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0); + } + } + + @Override + public boolean tryAutoSave() { + // todo: implement + return textPanel.tryAutoSave() && false; + } + + @Override + public boolean isEditing() { + return textPanel.isEditing() + || (genericSaveButton.isVisible() && genericSaveButton.isEnabled()) + || (metadataSaveButton.isVisible() && metadataSaveButton.isEnabled()); + } +}