diff --git a/src/com/jpexs/decompiler/flash/gui/DebugPanel.java b/src/com/jpexs/decompiler/flash/gui/DebugPanel.java index 2c223c83d..d36a02279 100644 --- a/src/com/jpexs/decompiler/flash/gui/DebugPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/DebugPanel.java @@ -1,508 +1,508 @@ -/* - * Copyright (C) 2010-2016 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.debugger.flash.Variable; -import com.jpexs.debugger.flash.messages.in.InBreakAtExt; -import com.jpexs.debugger.flash.messages.in.InConstantPool; -import com.jpexs.debugger.flash.messages.in.InFrame; -import com.jpexs.decompiler.flash.gui.DebuggerHandler.BreakListener; -import com.jpexs.decompiler.flash.gui.abc.ABCPanel; -import de.hameister.treetable.MyTreeTable; -import de.hameister.treetable.MyTreeTableModel; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.FlowLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.ArrayList; -import java.util.List; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTabbedPane; -import javax.swing.JTable; -import javax.swing.JTextArea; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.TreeModelEvent; -import javax.swing.event.TreeModelListener; -import javax.swing.table.DefaultTableModel; -import javax.swing.tree.TreePath; - -/** - * - * @author JPEXS - */ -public class DebugPanel extends JPanel { - - private MyTreeTable debugRegistersTable; - - private MyTreeTable debugLocalsTable; //JTable debugLocalsTable; - - private MyTreeTable debugScopeTable; - - private JTable callStackTable; - - private JTable stackTable; - - private JTable constantPoolTable; - - private JTabbedPane varTabs; - - private BreakListener listener; - - private JTextArea traceLogTextarea; - - private int logLength = 0; - - private List tabTypes = new ArrayList<>(); - - private boolean loading = false; - - public static enum SelectedTab { - - LOG, STACK, SCOPECHAIN, LOCALS, REGISTERS, CALLSTACK, CONSTANTPOOL - } - - public synchronized boolean isLoading() { - return loading; - } - - public synchronized void setLoading(boolean loading) { - this.loading = loading; - } - - private SelectedTab selectedTab = null; - - private void safeSetTreeModel(MyTreeTable tt, MyTreeTableModel tmodel) { - List> expanded = View.getExpandedNodes(tt.getTree()); - - int[] selRows = tt.getSelectedRows(); - - TreePath[] selPaths = new TreePath[selRows.length]; - for (int i = 0; i < selRows.length; i++) { - selPaths[i] = tt.getTree().getPathForRow(selRows[i]); - } - tt.setTreeModel(tmodel); - //tt.getTree().setRootVisible(false); - - View.expandTreeNodes(tt.getTree(), expanded); - for (int i = 0; i < selRows.length; i++) { - selRows[i] = tt.getTree().getRowForPath(selPaths[i]); - if (i == 0) { - tt.setRowSelectionInterval(selRows[i], selRows[i]); - } else { - tt.addRowSelectionInterval(selRows[i], selRows[i]); - } - } - - } - - public DebugPanel() { - super(new BorderLayout()); - debugRegistersTable = new MyTreeTable(new ABCPanel.VariablesTableModel(debugRegistersTable, new ArrayList<>(), new ArrayList<>()), false); - debugLocalsTable = new MyTreeTable(new ABCPanel.VariablesTableModel(debugLocalsTable, new ArrayList<>(), new ArrayList<>()), false); - - //Add watch feature, commented out. I tried it, but without success. I can't add watch in Flash Pro or FDB either. :-( - /* - //locales - debug.watch.add = Add watch to %name% - debug.watch.add.read = Read - debug.watch.add.write = Write - debug.watch.add.readwrite = Read+Write - - error.debug.watch.add = Cannot add watch to this variable. - - - MouseAdapter watchHandler = new MouseAdapter() { - - @Override - public void mousePressed(MouseEvent e) { - if (e.isPopupTrigger()) { - dopop(e); - } - } - - @Override - public void mouseReleased(MouseEvent e) { - if (e.isPopupTrigger()) { - dopop(e); - } - } - - private void dopop(MouseEvent e) { - if (debugLocalsTable.getSelectedRow() == -1) { - return; - } - Variable v = ((ABCPanel.VariablesTableModel) debugLocalsTable.getModel()).getVars().get(debugLocalsTable.getSelectedRow()); - final long v_id = ((ABCPanel.VariablesTableModel) debugLocalsTable.getModel()).getVarIds().get(debugLocalsTable.getSelectedRow()); - - JPopupMenu pm = new JPopupMenu(); - JMenu addWatchMenu = new JMenu(AppStrings.translate("debug.watch.add").replace("%name%", v.name)); - JMenuItem watchReadMenuItem = new JMenuItem(AppStrings.translate("debug.watch.add.read")); - watchReadMenuItem.addActionListener((ActionEvent e1) -> { - if (!Main.addWatch(v, v_id, true, false)) { - View.showMessageDialog(DebugPanel.this, AppStrings.translate("debug.watch.add"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - }); - JMenuItem watchWriteMenuItem = new JMenuItem(AppStrings.translate("debug.watch.add.write")); - watchWriteMenuItem.addActionListener((ActionEvent e1) -> { - if (!Main.addWatch(v, v_id, false, true)) { - View.showMessageDialog(DebugPanel.this, AppStrings.translate("debug.watch.add"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - }); - JMenuItem watchReadWriteMenuItem = new JMenuItem(AppStrings.translate("debug.watch.add.readwrite")); - watchReadWriteMenuItem.addActionListener((ActionEvent e1) -> { - if (!Main.addWatch(v, v_id, true, true)) { - View.showMessageDialog(DebugPanel.this, AppStrings.translate("debug.watch.add"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - }); - - addWatchMenu.add(watchReadMenuItem); - addWatchMenu.add(watchWriteMenuItem); - addWatchMenu.add(watchReadWriteMenuItem); - pm.add(addWatchMenu); - - pm.show(e.getComponent(), e.getX(), e.getY()); - } - }; - - debugLocalsTable.addMouseListener(watchHandler); - debugScopeTable.addMouseListener(watchHandler); - - */ - debugScopeTable = new MyTreeTable(new ABCPanel.VariablesTableModel(debugScopeTable, new ArrayList<>(), new ArrayList<>()), false); - - callStackTable = new JTable(); - stackTable = new JTable(); - constantPoolTable = new JTable(); - traceLogTextarea = new JTextArea(); - traceLogTextarea.setEditable(false); - traceLogTextarea.setOpaque(false); - traceLogTextarea.setFont(new JLabel().getFont()); - traceLogTextarea.setBackground(Color.white); - - Main.getDebugHandler().addTraceListener(new DebuggerHandler.TraceListener() { - - @Override - public void trace(String... val) { - for (String s : val) { - String add = "trace: " + s + "\r\n"; - boolean wasEmpty = logLength == 0; - logLength += add.length(); - traceLogTextarea.append(add); - try { - traceLogTextarea.setCaretPosition(logLength); - } catch (IllegalArgumentException iex) { - //ignore - } - if (wasEmpty) { - refresh(); - } - } - } - }); - - Main.getDebugHandler().addConnectionListener(new DebuggerHandler.ConnectionListener() { - - @Override - public void connected() { - } - - @Override - public void disconnected() { - refresh(); - } - }); - - Main.getDebugHandler().addBreakListener(listener = new DebuggerHandler.BreakListener() { - - @Override - public void doContinue() { - View.execInEventDispatch(new Runnable() { - - @Override - public void run() { - refresh(); - } - - }); - } - - @Override - public void breakAt(String scriptName, int line, int classIndex, int traitIndex, int methodIndex) { - View.execInEventDispatch(new Runnable() { - - @Override - public void run() { - refresh(); - } - }); - - } - }); - - varTabs = new JTabbedPane(); - varTabs.addChangeListener(new ChangeListener() { - - @Override - public void stateChanged(ChangeEvent e) { - if (e.getSource() == varTabs) { - if (isLoading()) { - return; - } - synchronized (DebugPanel.this) { - int si = varTabs.getSelectedIndex(); - if (si > -1 && si < tabTypes.size()) { - selectedTab = tabTypes.get(si); - } - } - } - } - }); - - add(new HeaderLabel(AppStrings.translate("debugpanel.header")), BorderLayout.NORTH); - add(varTabs, BorderLayout.CENTER); - } - - public void refresh() { - - View.execInEventDispatch(new Runnable() { - - @Override - public void run() { - setLoading(true); - synchronized (DebugPanel.this) { - - SelectedTab oldSel = selectedTab; - InFrame f = Main.getDebugHandler().getFrame(); - if (f != null) { - - List regVarIds = new ArrayList<>(); - for (int i = 0; i < f.registers.size(); i++) { - regVarIds.add(0L); - } - safeSetTreeModel(debugRegistersTable, new ABCPanel.VariablesTableModel(debugRegistersTable, f.registers, regVarIds)); - List locals = new ArrayList<>(); - locals.addAll(f.arguments); - locals.addAll(f.variables); - - List localIds = new ArrayList<>(); - localIds.addAll(f.argumentFrameIds); - localIds.addAll(f.frameIds); - - safeSetTreeModel(debugLocalsTable, new ABCPanel.VariablesTableModel(debugLocalsTable, locals, localIds)); - safeSetTreeModel(debugScopeTable, new ABCPanel.VariablesTableModel(debugScopeTable, f.scopeChain, f.scopeChainFrameIds)); - - /*TableModelListener refreshListener = new TableModelListener() { - @Override - public void tableChanged(TableModelEvent e) { - Main.getDebugHandler().refreshFrame(); - refresh(); - } - };*/ - TreeModelListener refreshListener = new TreeModelListener() { - @Override - public void treeNodesChanged(TreeModelEvent e) { - Main.getDebugHandler().refreshFrame(); - refresh(); - } - - @Override - public void treeNodesInserted(TreeModelEvent e) { - Main.getDebugHandler().refreshFrame(); - refresh(); - } - - @Override - public void treeNodesRemoved(TreeModelEvent e) { - Main.getDebugHandler().refreshFrame(); - refresh(); - } - - @Override - public void treeStructureChanged(TreeModelEvent e) { - Main.getDebugHandler().refreshFrame(); - refresh(); - } - }; - debugLocalsTable.getTreeTableModel().addTreeModelListener(refreshListener); - debugScopeTable.getTreeTableModel().addTreeModelListener(refreshListener); - } else { - debugRegistersTable.setTreeModel(new ABCPanel.VariablesTableModel(debugRegistersTable, new ArrayList<>(), new ArrayList<>())); - debugLocalsTable.setTreeModel(new ABCPanel.VariablesTableModel(debugLocalsTable, new ArrayList<>(), new ArrayList<>())); - debugScopeTable.setTreeModel(new ABCPanel.VariablesTableModel(debugScopeTable, new ArrayList<>(), new ArrayList<>())); - } - InBreakAtExt info = Main.getDebugHandler().getBreakInfo(); - if (info != null) { - //InBreakReason reason = Main.getDebugHandler().getBreakReason(); - List callStackFiles = new ArrayList<>(); - List callStackLines = new ArrayList<>(); - - callStackFiles.add(Main.getDebugHandler().moduleToString(info.file)); - callStackLines.add(info.line); - - for (int i = 0; i < info.files.size(); i++) { - callStackFiles.add(Main.getDebugHandler().moduleToString(info.files.get(i))); - callStackLines.add(info.lines.get(i)); - } - Object[][] data = new Object[callStackFiles.size()][2]; - for (int i = 0; i < callStackFiles.size(); i++) { - data[i][0] = callStackFiles.get(i); - data[i][1] = callStackLines.get(i); - } - - DefaultTableModel tm = new DefaultTableModel(data, new Object[]{ - AppStrings.translate("callStack.header.file"), - AppStrings.translate("callStack.header.line") - }) { - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - - }; - callStackTable.setModel(tm); - - Object[][] data2 = new Object[info.stacks.size()][1]; - for (int i = 0; i < info.stacks.size(); i++) { - data2[i][0] = info.stacks.get(i); - } - stackTable.setModel(new DefaultTableModel(data2, new Object[]{AppStrings.translate("stack.header.item")}) { - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - - }); - } else { - callStackTable.setModel(new DefaultTableModel()); - stackTable.setModel(new DefaultTableModel()); - } - InConstantPool cpool = Main.getDebugHandler().getConstantPool(); - if (cpool != null) { - Object[][] data2 = new Object[cpool.vars.size()][2]; - for (int i = 0; i < cpool.vars.size(); i++) { - data2[i][0] = cpool.ids.get(i); - data2[i][1] = cpool.vars.get(i).value; - } - constantPoolTable.setModel(new DefaultTableModel(data2, new Object[]{ - AppStrings.translate("constantpool.header.id"), - AppStrings.translate("constantpool.header.value") - }) { - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - - }); - } - - varTabs.removeAll(); - tabTypes.clear(); - JPanel pa; - if (debugRegistersTable.getRowCount() > 0) { - tabTypes.add(SelectedTab.REGISTERS); - pa = new JPanel(new BorderLayout()); - pa.add(new JScrollPane(debugRegistersTable), BorderLayout.CENTER); - varTabs.addTab(AppStrings.translate("variables.header.registers"), pa); - } - if (debugLocalsTable.getRowCount() > 0) { - tabTypes.add(SelectedTab.LOCALS); - - pa = new JPanel(new BorderLayout()); - pa.add(new JScrollPane(debugLocalsTable), BorderLayout.CENTER); - varTabs.addTab(AppStrings.translate("variables.header.locals"), pa); - } - - if (debugScopeTable.getRowCount() > 0) { - tabTypes.add(SelectedTab.SCOPECHAIN); - - pa = new JPanel(new BorderLayout()); - pa.add(new JScrollPane(debugScopeTable), BorderLayout.CENTER); - varTabs.addTab(AppStrings.translate("variables.header.scopeChain"), pa); - } - - if (constantPoolTable.getRowCount() > 0) { - tabTypes.add(SelectedTab.CONSTANTPOOL); - - pa = new JPanel(new BorderLayout()); - pa.add(new JScrollPane(constantPoolTable), BorderLayout.CENTER); - varTabs.addTab(AppStrings.translate("constantpool.header"), pa); - } - - if (callStackTable.getRowCount() > 0) { - tabTypes.add(SelectedTab.CALLSTACK); - - pa = new JPanel(new BorderLayout()); - pa.add(new JScrollPane(callStackTable), BorderLayout.CENTER); - varTabs.addTab(AppStrings.translate("callStack.header"), pa); - } - if (stackTable.getRowCount() > 0) { - tabTypes.add(SelectedTab.STACK); - - pa = new JPanel(new BorderLayout()); - pa.add(new JScrollPane(stackTable), BorderLayout.CENTER); - varTabs.addTab(AppStrings.translate("stack.header"), pa); - } - if (logLength > 0) { - tabTypes.add(SelectedTab.LOG); - - pa = new JPanel(new BorderLayout()); - pa.add(new JScrollPane(traceLogTextarea), BorderLayout.CENTER); - JButton clearButton = new JButton(AppStrings.translate("debuglog.button.clear")); - clearButton.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - traceLogTextarea.setText(""); - logLength = 0; - refresh(); - } - }); - JPanel butPanel = new JPanel(new FlowLayout()); - butPanel.add(clearButton); - pa.add(butPanel, BorderLayout.SOUTH); - varTabs.addTab(AppStrings.translate("debuglog.header"), pa); - } - boolean newVisible = !tabTypes.isEmpty(); - if (newVisible != isVisible()) { - setVisible(newVisible); - } - if (!tabTypes.isEmpty()) { - if (oldSel != null && !tabTypes.contains(oldSel)) { - oldSel = null; - } - } - if (oldSel != null) { - selectedTab = oldSel; - varTabs.setSelectedIndex(tabTypes.indexOf(selectedTab)); - } - setLoading(false); - } - - } - }); - - } - - public void dispose() { - Main.getDebugHandler().removeBreakListener(listener); - } -} +/* + * Copyright (C) 2010-2016 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.debugger.flash.Variable; +import com.jpexs.debugger.flash.messages.in.InBreakAtExt; +import com.jpexs.debugger.flash.messages.in.InConstantPool; +import com.jpexs.debugger.flash.messages.in.InFrame; +import com.jpexs.decompiler.flash.gui.DebuggerHandler.BreakListener; +import com.jpexs.decompiler.flash.gui.abc.ABCPanel; +import de.hameister.treetable.MyTreeTable; +import de.hameister.treetable.MyTreeTableModel; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; +import javax.swing.JTable; +import javax.swing.JTextArea; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; +import javax.swing.table.DefaultTableModel; +import javax.swing.tree.TreePath; + +/** + * + * @author JPEXS + */ +public class DebugPanel extends JPanel { + + private MyTreeTable debugRegistersTable; + + private MyTreeTable debugLocalsTable; //JTable debugLocalsTable; + + private MyTreeTable debugScopeTable; + + private JTable callStackTable; + + private JTable stackTable; + + private JTable constantPoolTable; + + private JTabbedPane varTabs; + + private BreakListener listener; + + private JTextArea traceLogTextarea; + + private int logLength = 0; + + private List tabTypes = new ArrayList<>(); + + private boolean loading = false; + + public static enum SelectedTab { + + LOG, STACK, SCOPECHAIN, LOCALS, REGISTERS, CALLSTACK, CONSTANTPOOL + } + + public synchronized boolean isLoading() { + return loading; + } + + public synchronized void setLoading(boolean loading) { + this.loading = loading; + } + + private SelectedTab selectedTab = null; + + private void safeSetTreeModel(MyTreeTable tt, MyTreeTableModel tmodel) { + List> expanded = View.getExpandedNodes(tt.getTree()); + + int[] selRows = tt.getSelectedRows(); + + TreePath[] selPaths = new TreePath[selRows.length]; + for (int i = 0; i < selRows.length; i++) { + selPaths[i] = tt.getTree().getPathForRow(selRows[i]); + } + tt.setTreeModel(tmodel); + //tt.getTree().setRootVisible(false); + + View.expandTreeNodes(tt.getTree(), expanded); + for (int i = 0; i < selRows.length; i++) { + selRows[i] = tt.getTree().getRowForPath(selPaths[i]); + if (i == 0) { + tt.setRowSelectionInterval(selRows[i], selRows[i]); + } else { + tt.addRowSelectionInterval(selRows[i], selRows[i]); + } + } + + } + + public DebugPanel() { + super(new BorderLayout()); + debugRegistersTable = new MyTreeTable(new ABCPanel.VariablesTableModel(debugRegistersTable, new ArrayList<>(), new ArrayList<>()), false); + debugLocalsTable = new MyTreeTable(new ABCPanel.VariablesTableModel(debugLocalsTable, new ArrayList<>(), new ArrayList<>()), false); + + //Add watch feature, commented out. I tried it, but without success. I can't add watch in Flash Pro or FDB either. :-( + /* + //locales + debug.watch.add = Add watch to %name% + debug.watch.add.read = Read + debug.watch.add.write = Write + debug.watch.add.readwrite = Read+Write + + error.debug.watch.add = Cannot add watch to this variable. + + + MouseAdapter watchHandler = new MouseAdapter() { + + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + dopop(e); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + dopop(e); + } + } + + private void dopop(MouseEvent e) { + if (debugLocalsTable.getSelectedRow() == -1) { + return; + } + Variable v = ((ABCPanel.VariablesTableModel) debugLocalsTable.getModel()).getVars().get(debugLocalsTable.getSelectedRow()); + final long v_id = ((ABCPanel.VariablesTableModel) debugLocalsTable.getModel()).getVarIds().get(debugLocalsTable.getSelectedRow()); + + JPopupMenu pm = new JPopupMenu(); + JMenu addWatchMenu = new JMenu(AppStrings.translate("debug.watch.add").replace("%name%", v.name)); + JMenuItem watchReadMenuItem = new JMenuItem(AppStrings.translate("debug.watch.add.read")); + watchReadMenuItem.addActionListener((ActionEvent e1) -> { + if (!Main.addWatch(v, v_id, true, false)) { + View.showMessageDialog(DebugPanel.this, AppStrings.translate("debug.watch.add"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + }); + JMenuItem watchWriteMenuItem = new JMenuItem(AppStrings.translate("debug.watch.add.write")); + watchWriteMenuItem.addActionListener((ActionEvent e1) -> { + if (!Main.addWatch(v, v_id, false, true)) { + View.showMessageDialog(DebugPanel.this, AppStrings.translate("debug.watch.add"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + }); + JMenuItem watchReadWriteMenuItem = new JMenuItem(AppStrings.translate("debug.watch.add.readwrite")); + watchReadWriteMenuItem.addActionListener((ActionEvent e1) -> { + if (!Main.addWatch(v, v_id, true, true)) { + View.showMessageDialog(DebugPanel.this, AppStrings.translate("debug.watch.add"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + }); + + addWatchMenu.add(watchReadMenuItem); + addWatchMenu.add(watchWriteMenuItem); + addWatchMenu.add(watchReadWriteMenuItem); + pm.add(addWatchMenu); + + pm.show(e.getComponent(), e.getX(), e.getY()); + } + }; + + debugLocalsTable.addMouseListener(watchHandler); + debugScopeTable.addMouseListener(watchHandler); + + */ + debugScopeTable = new MyTreeTable(new ABCPanel.VariablesTableModel(debugScopeTable, new ArrayList<>(), new ArrayList<>()), false); + + callStackTable = new JTable(); + stackTable = new JTable(); + constantPoolTable = new JTable(); + traceLogTextarea = new JTextArea(); + traceLogTextarea.setEditable(false); + traceLogTextarea.setOpaque(false); + traceLogTextarea.setFont(new JLabel().getFont()); + traceLogTextarea.setBackground(Color.white); + + Main.getDebugHandler().addTraceListener(new DebuggerHandler.TraceListener() { + + @Override + public void trace(String... val) { + for (String s : val) { + String add = "trace: " + s + "\r\n"; + boolean wasEmpty = logLength == 0; + logLength += add.length(); + traceLogTextarea.append(add); + try { + traceLogTextarea.setCaretPosition(logLength); + } catch (IllegalArgumentException iex) { + //ignore + } + if (wasEmpty) { + refresh(); + } + } + } + }); + + Main.getDebugHandler().addConnectionListener(new DebuggerHandler.ConnectionListener() { + + @Override + public void connected() { + } + + @Override + public void disconnected() { + refresh(); + } + }); + + Main.getDebugHandler().addBreakListener(listener = new DebuggerHandler.BreakListener() { + + @Override + public void doContinue() { + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + refresh(); + } + + }); + } + + @Override + public void breakAt(String scriptName, int line, int classIndex, int traitIndex, int methodIndex) { + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + refresh(); + } + }); + + } + }); + + varTabs = new JTabbedPane(); + varTabs.addChangeListener(new ChangeListener() { + + @Override + public void stateChanged(ChangeEvent e) { + if (e.getSource() == varTabs) { + if (isLoading()) { + return; + } + synchronized (DebugPanel.this) { + int si = varTabs.getSelectedIndex(); + if (si > -1 && si < tabTypes.size()) { + selectedTab = tabTypes.get(si); + } + } + } + } + }); + + add(new HeaderLabel(AppStrings.translate("debugpanel.header")), BorderLayout.NORTH); + add(varTabs, BorderLayout.CENTER); + } + + public void refresh() { + + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + setLoading(true); + synchronized (DebugPanel.this) { + + SelectedTab oldSel = selectedTab; + InFrame f = Main.getDebugHandler().getFrame(); + if (f != null) { + + List regVarIds = new ArrayList<>(); + for (int i = 0; i < f.registers.size(); i++) { + regVarIds.add(0L); + } + safeSetTreeModel(debugRegistersTable, new ABCPanel.VariablesTableModel(debugRegistersTable, f.registers, regVarIds)); + List locals = new ArrayList<>(); + locals.addAll(f.arguments); + locals.addAll(f.variables); + + List localIds = new ArrayList<>(); + localIds.addAll(f.argumentFrameIds); + localIds.addAll(f.frameIds); + + safeSetTreeModel(debugLocalsTable, new ABCPanel.VariablesTableModel(debugLocalsTable, locals, localIds)); + safeSetTreeModel(debugScopeTable, new ABCPanel.VariablesTableModel(debugScopeTable, f.scopeChain, f.scopeChainFrameIds)); + + /*TableModelListener refreshListener = new TableModelListener() { + @Override + public void tableChanged(TableModelEvent e) { + Main.getDebugHandler().refreshFrame(); + refresh(); + } + };*/ + TreeModelListener refreshListener = new TreeModelListener() { + @Override + public void treeNodesChanged(TreeModelEvent e) { + Main.getDebugHandler().refreshFrame(); + refresh(); + } + + @Override + public void treeNodesInserted(TreeModelEvent e) { + Main.getDebugHandler().refreshFrame(); + refresh(); + } + + @Override + public void treeNodesRemoved(TreeModelEvent e) { + Main.getDebugHandler().refreshFrame(); + refresh(); + } + + @Override + public void treeStructureChanged(TreeModelEvent e) { + Main.getDebugHandler().refreshFrame(); + refresh(); + } + }; + debugLocalsTable.getTreeTableModel().addTreeModelListener(refreshListener); + debugScopeTable.getTreeTableModel().addTreeModelListener(refreshListener); + } else { + debugRegistersTable.setTreeModel(new ABCPanel.VariablesTableModel(debugRegistersTable, new ArrayList<>(), new ArrayList<>())); + debugLocalsTable.setTreeModel(new ABCPanel.VariablesTableModel(debugLocalsTable, new ArrayList<>(), new ArrayList<>())); + debugScopeTable.setTreeModel(new ABCPanel.VariablesTableModel(debugScopeTable, new ArrayList<>(), new ArrayList<>())); + } + InBreakAtExt info = Main.getDebugHandler().getBreakInfo(); + if (info != null) { + //InBreakReason reason = Main.getDebugHandler().getBreakReason(); + List callStackFiles = new ArrayList<>(); + List callStackLines = new ArrayList<>(); + + callStackFiles.add(Main.getDebugHandler().moduleToString(info.file)); + callStackLines.add(info.line); + + for (int i = 0; i < info.files.size(); i++) { + callStackFiles.add(Main.getDebugHandler().moduleToString(info.files.get(i))); + callStackLines.add(info.lines.get(i)); + } + Object[][] data = new Object[callStackFiles.size()][2]; + for (int i = 0; i < callStackFiles.size(); i++) { + data[i][0] = callStackFiles.get(i); + data[i][1] = callStackLines.get(i); + } + + DefaultTableModel tm = new DefaultTableModel(data, new Object[]{ + AppStrings.translate("callStack.header.file"), + AppStrings.translate("callStack.header.line") + }) { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + + }; + callStackTable.setModel(tm); + + Object[][] data2 = new Object[info.stacks.size()][1]; + for (int i = 0; i < info.stacks.size(); i++) { + data2[i][0] = info.stacks.get(i); + } + stackTable.setModel(new DefaultTableModel(data2, new Object[]{AppStrings.translate("stack.header.item")}) { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + + }); + } else { + callStackTable.setModel(new DefaultTableModel()); + stackTable.setModel(new DefaultTableModel()); + } + InConstantPool cpool = Main.getDebugHandler().getConstantPool(); + if (cpool != null) { + Object[][] data2 = new Object[cpool.vars.size()][2]; + for (int i = 0; i < cpool.vars.size(); i++) { + data2[i][0] = cpool.ids.get(i); + data2[i][1] = cpool.vars.get(i).value; + } + constantPoolTable.setModel(new DefaultTableModel(data2, new Object[]{ + AppStrings.translate("constantpool.header.id"), + AppStrings.translate("constantpool.header.value") + }) { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + + }); + } + + varTabs.removeAll(); + tabTypes.clear(); + JPanel pa; + if (debugRegistersTable.getRowCount() > 0) { + tabTypes.add(SelectedTab.REGISTERS); + pa = new JPanel(new BorderLayout()); + pa.add(new JScrollPane(debugRegistersTable), BorderLayout.CENTER); + varTabs.addTab(AppStrings.translate("variables.header.registers"), pa); + } + if (debugLocalsTable.getRowCount() > 0) { + tabTypes.add(SelectedTab.LOCALS); + + pa = new JPanel(new BorderLayout()); + pa.add(new JScrollPane(debugLocalsTable), BorderLayout.CENTER); + varTabs.addTab(AppStrings.translate("variables.header.locals"), pa); + } + + if (debugScopeTable.getRowCount() > 0) { + tabTypes.add(SelectedTab.SCOPECHAIN); + + pa = new JPanel(new BorderLayout()); + pa.add(new JScrollPane(debugScopeTable), BorderLayout.CENTER); + varTabs.addTab(AppStrings.translate("variables.header.scopeChain"), pa); + } + + if (constantPoolTable.getRowCount() > 0) { + tabTypes.add(SelectedTab.CONSTANTPOOL); + + pa = new JPanel(new BorderLayout()); + pa.add(new JScrollPane(constantPoolTable), BorderLayout.CENTER); + varTabs.addTab(AppStrings.translate("constantpool.header"), pa); + } + + if (callStackTable.getRowCount() > 0) { + tabTypes.add(SelectedTab.CALLSTACK); + + pa = new JPanel(new BorderLayout()); + pa.add(new JScrollPane(callStackTable), BorderLayout.CENTER); + varTabs.addTab(AppStrings.translate("callStack.header"), pa); + } + if (stackTable.getRowCount() > 0) { + tabTypes.add(SelectedTab.STACK); + + pa = new JPanel(new BorderLayout()); + pa.add(new JScrollPane(stackTable), BorderLayout.CENTER); + varTabs.addTab(AppStrings.translate("stack.header"), pa); + } + if (logLength > 0) { + tabTypes.add(SelectedTab.LOG); + + pa = new JPanel(new BorderLayout()); + pa.add(new JScrollPane(traceLogTextarea), BorderLayout.CENTER); + JButton clearButton = new JButton(AppStrings.translate("debuglog.button.clear")); + clearButton.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + traceLogTextarea.setText(""); + logLength = 0; + refresh(); + } + }); + JPanel butPanel = new JPanel(new FlowLayout()); + butPanel.add(clearButton); + pa.add(butPanel, BorderLayout.SOUTH); + varTabs.addTab(AppStrings.translate("debuglog.header"), pa); + } + boolean newVisible = !tabTypes.isEmpty(); + if (newVisible != isVisible()) { + setVisible(newVisible); + } + if (!tabTypes.isEmpty()) { + if (oldSel != null && !tabTypes.contains(oldSel)) { + oldSel = null; + } + } + if (oldSel != null) { + selectedTab = oldSel; + varTabs.setSelectedIndex(tabTypes.indexOf(selectedTab)); + } + setLoading(false); + } + + } + }); + + } + + public void dispose() { + Main.getDebugHandler().removeBreakListener(listener); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/ExportDialog.java b/src/com/jpexs/decompiler/flash/gui/ExportDialog.java index fea512776..447234cba 100644 --- a/src/com/jpexs/decompiler/flash/gui/ExportDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/ExportDialog.java @@ -1,366 +1,366 @@ -/* - * Copyright (C) 2010-2016 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.abc.ScriptPack; -import com.jpexs.decompiler.flash.configuration.Configuration; -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.gui.tagtree.TagTreeModel; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.tags.base.ButtonTag; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.tags.base.SoundTag; -import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import java.awt.BorderLayout; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.event.ActionEvent; -import java.util.Arrays; -import java.util.List; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; - -/** - * - * @author JPEXS - */ -public class ExportDialog extends AppDialog { - - private int result = ERROR_OPTION; - - String[] optionNames = { - TagTreeModel.FOLDER_SHAPES, - TagTreeModel.FOLDER_TEXTS, - TagTreeModel.FOLDER_IMAGES, - TagTreeModel.FOLDER_MOVIES, - TagTreeModel.FOLDER_SOUNDS, - TagTreeModel.FOLDER_SCRIPTS, - TagTreeModel.FOLDER_BINARY_DATA, - TagTreeModel.FOLDER_FRAMES, - TagTreeModel.FOLDER_SPRITES, - TagTreeModel.FOLDER_BUTTONS, - TagTreeModel.FOLDER_FONTS, - TagTreeModel.FOLDER_MORPHSHAPES, - "symbolclass" - }; - - //Display options only when these classes found - Class[][] objClasses = { - {ShapeTag.class}, - {TextTag.class}, - {ImageTag.class}, - {DefineVideoStreamTag.class}, - {SoundTag.class}, - {ASMSource.class, ScriptPack.class}, - {DefineBinaryDataTag.class}, - {Frame.class}, - {Frame.class}, - {ButtonTag.class}, - {FontTag.class}, - {MorphShapeTag.class}, - {SymbolClassTypeTag.class} - }; - - //Enum classes for values - Class[] optionClasses = { - ShapeExportMode.class, - TextExportMode.class, - ImageExportMode.class, - MovieExportMode.class, - SoundExportMode.class, - ScriptExportMode.class, - BinaryDataExportMode.class, - FrameExportMode.class, - SpriteExportMode.class, - ButtonExportMode.class, - FontExportMode.class, - MorphShapeExportMode.class, - SymbolClassExportMode.class - }; - - Class[] zoomClasses = { - ShapeExportMode.class, - TextExportMode.class, - FrameExportMode.class, - MorphShapeExportMode.class - }; - - private final JComboBox[] combos; - - private final JCheckBox[] checkBoxes; - - private final JCheckBox selectAllCheckBox; - - private JTextField zoomTextField = new JTextField(); - - public E getValue(Class option) { - for (int i = 0; i < optionClasses.length; i++) { - if (option == optionClasses[i]) { - E[] values = option.getEnumConstants(); - return values[combos[i].getSelectedIndex()]; - } - } - - return null; - } - - public boolean isOptionEnabled(Class option) { - for (int i = 0; i < optionClasses.length; i++) { - if (option == optionClasses[i]) { - return checkBoxes[i].isSelected(); - } - } - - return false; - } - - public double getZoom() { - return Double.parseDouble(zoomTextField.getText()) / 100; - } - - private void saveConfig() { - StringBuilder cfg = new StringBuilder(); - for (int i = 0; i < optionNames.length; i++) { - int selIndex = combos[i].getSelectedIndex(); - Class c = optionClasses[i]; - Object[] vals = c.getEnumConstants(); - String key = optionNames[i] + "." + vals[selIndex].toString().toLowerCase(); - if (i > 0) { - cfg.append(","); - } - cfg.append(key); - } - - Configuration.lastSelectedExportZoom.set(Double.parseDouble(zoomTextField.getText()) / 100); - Configuration.lastSelectedExportFormats.set(cfg.toString()); - } - - public ExportDialog(List exportables) { - setTitle(translate("dialog.title")); - setResizable(false); - - Container cnt = getContentPane(); - cnt.setLayout(new BorderLayout()); - JPanel comboPanel = new JPanel(null); - int labWidth = 0; - boolean[] exportableExistsArray = new boolean[optionNames.length]; - for (int i = 0; i < optionNames.length; i++) { - boolean exportableExists = false; - if (exportables == null) { - exportableExists = true; - } else { - for (TreeItem e : exportables) { - for (int j = 0; j < objClasses[i].length; j++) { - if (objClasses[i][j].isInstance(e)) { - exportableExists = true; - } - } - } - } - - if (!exportableExists) { - continue; - } - - exportableExistsArray[i] = true; - - JLabel label = new JLabel(translate(optionNames[i])); - if (label.getPreferredSize().width > labWidth) { - labWidth = label.getPreferredSize().width; - } - } - - String exportFormatsStr = Configuration.lastSelectedExportFormats.get(); - if ("".equals(exportFormatsStr)) { - exportFormatsStr = null; - } - - String[] exportFormatsArr = new String[0]; - if (exportFormatsStr != null) { - if (exportFormatsStr.contains(",")) { - exportFormatsArr = exportFormatsStr.split(","); - } else { - exportFormatsArr = new String[]{exportFormatsStr}; - } - - } - - int comboWidth = 200; - int checkBoxWidth; - int top = 10; - - List exportFormats = Arrays.asList(exportFormatsArr); - combos = new JComboBox[optionNames.length]; - checkBoxes = new JCheckBox[optionNames.length]; - selectAllCheckBox = new JCheckBox(); - checkBoxWidth = selectAllCheckBox.getPreferredSize().width; - selectAllCheckBox.setBounds(10 + labWidth + 10 + comboWidth + 10, top, checkBoxWidth, selectAllCheckBox.getPreferredSize().height); - selectAllCheckBox.setSelected(true); - selectAllCheckBox.addActionListener((ActionEvent e) -> { - boolean selected = selectAllCheckBox.isSelected(); - for (JCheckBox checkBox : checkBoxes) { - if (checkBox != null) { - checkBox.setSelected(selected); - } - } - }); - comboPanel.add(selectAllCheckBox); - top += selectAllCheckBox.getHeight(); - - boolean zoomable = false; - for (int i = 0; i < optionNames.length; i++) { - Class c = optionClasses[i]; - Object[] vals = c.getEnumConstants(); - String[] names = new String[vals.length]; - int itemIndex = -1; - for (int j = 0; j < vals.length; j++) { - - String key = optionNames[i] + "." + vals[j].toString().toLowerCase(); - if (exportFormats.contains(key)) { - itemIndex = j; - } - names[j] = translate(key); - } - - combos[i] = new JComboBox<>(names); - if (itemIndex > -1) { - combos[i].setSelectedIndex(itemIndex); - } - - combos[i].setBounds(10 + labWidth + 10, top, comboWidth, combos[i].getPreferredSize().height); - - checkBoxes[i] = new JCheckBox(); - checkBoxes[i].setBounds(10 + labWidth + 10 + comboWidth + 10, top, checkBoxWidth, checkBoxes[i].getPreferredSize().height); - checkBoxes[i].setSelected(true); - - if (!exportableExistsArray[i]) { - continue; - } - - if (Arrays.asList(zoomClasses).contains(c)) { - zoomable = true; - } - - JLabel lab = new JLabel(translate(optionNames[i])); - lab.setBounds(10, top, lab.getPreferredSize().width, lab.getPreferredSize().height); - comboPanel.add(lab); - comboPanel.add(checkBoxes[i]); - comboPanel.add(combos[i]); - top += combos[i].getHeight(); - } - - int zoomWidth = 50; - if (zoomable) { - top += 2; - JLabel zlab = new JLabel(translate("zoom")); - zlab.setBounds(10, top + 4, zlab.getPreferredSize().width, zlab.getPreferredSize().height); - zoomTextField.setBounds(10 + labWidth + 10, top, zoomWidth, zoomTextField.getPreferredSize().height); - JLabel pctLabel = new JLabel(translate("zoom.percent")); - pctLabel.setBounds(10 + labWidth + 10 + zoomWidth + 5, top + 4, 20, pctLabel.getPreferredSize().height); - - comboPanel.add(zlab); - comboPanel.add(zoomTextField); - comboPanel.add(pctLabel); - top += zoomTextField.getHeight(); - } - - Dimension dim = new Dimension(10 + labWidth + 10 + checkBoxWidth + 10 + comboWidth + 10, top + 10); - comboPanel.setMinimumSize(dim); - comboPanel.setPreferredSize(dim); - cnt.add(comboPanel, BorderLayout.CENTER); - - JPanel buttonsPanel = new JPanel(new FlowLayout()); - JButton okButton = new JButton(translate("button.ok")); - okButton.addActionListener(this::okButtonActionPerformed); - - JButton cancelButton = new JButton(translate("button.cancel")); - cancelButton.addActionListener(this::cancelButtonActionPerformed); - - buttonsPanel.add(okButton); - buttonsPanel.add(cancelButton); - - cnt.add(buttonsPanel, BorderLayout.SOUTH); - pack(); - View.centerScreen(this); - View.setWindowIcon(this); - getRootPane().setDefaultButton(okButton); - setModal(true); - String pct = "" + Configuration.lastSelectedExportZoom.get() * 100; - if (pct.endsWith(".0")) { - pct = pct.substring(0, pct.length() - 2); - } - - zoomTextField.setText(pct); - } - - @Override - public void setVisible(boolean b) { - if (b) { - result = ERROR_OPTION; - } - super.setVisible(b); - } - - private void okButtonActionPerformed(ActionEvent evt) { - result = OK_OPTION; - try { - saveConfig(); - } catch (NumberFormatException nfe) { - JOptionPane.showMessageDialog(ExportDialog.this, translate("zoom.invalid"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - zoomTextField.requestFocusInWindow(); - return; - } - - setVisible(false); - } - - private void cancelButtonActionPerformed(ActionEvent evt) { - result = CANCEL_OPTION; - setVisible(false); - } - - public int showExportDialog() { - setVisible(true); - return result; - } -} +/* + * Copyright (C) 2010-2016 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.abc.ScriptPack; +import com.jpexs.decompiler.flash.configuration.Configuration; +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.gui.tagtree.TagTreeModel; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.tags.base.SoundTag; +import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.util.Arrays; +import java.util.List; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; + +/** + * + * @author JPEXS + */ +public class ExportDialog extends AppDialog { + + private int result = ERROR_OPTION; + + String[] optionNames = { + TagTreeModel.FOLDER_SHAPES, + TagTreeModel.FOLDER_TEXTS, + TagTreeModel.FOLDER_IMAGES, + TagTreeModel.FOLDER_MOVIES, + TagTreeModel.FOLDER_SOUNDS, + TagTreeModel.FOLDER_SCRIPTS, + TagTreeModel.FOLDER_BINARY_DATA, + TagTreeModel.FOLDER_FRAMES, + TagTreeModel.FOLDER_SPRITES, + TagTreeModel.FOLDER_BUTTONS, + TagTreeModel.FOLDER_FONTS, + TagTreeModel.FOLDER_MORPHSHAPES, + "symbolclass" + }; + + //Display options only when these classes found + Class[][] objClasses = { + {ShapeTag.class}, + {TextTag.class}, + {ImageTag.class}, + {DefineVideoStreamTag.class}, + {SoundTag.class}, + {ASMSource.class, ScriptPack.class}, + {DefineBinaryDataTag.class}, + {Frame.class}, + {Frame.class}, + {ButtonTag.class}, + {FontTag.class}, + {MorphShapeTag.class}, + {SymbolClassTypeTag.class} + }; + + //Enum classes for values + Class[] optionClasses = { + ShapeExportMode.class, + TextExportMode.class, + ImageExportMode.class, + MovieExportMode.class, + SoundExportMode.class, + ScriptExportMode.class, + BinaryDataExportMode.class, + FrameExportMode.class, + SpriteExportMode.class, + ButtonExportMode.class, + FontExportMode.class, + MorphShapeExportMode.class, + SymbolClassExportMode.class + }; + + Class[] zoomClasses = { + ShapeExportMode.class, + TextExportMode.class, + FrameExportMode.class, + MorphShapeExportMode.class + }; + + private final JComboBox[] combos; + + private final JCheckBox[] checkBoxes; + + private final JCheckBox selectAllCheckBox; + + private JTextField zoomTextField = new JTextField(); + + public E getValue(Class option) { + for (int i = 0; i < optionClasses.length; i++) { + if (option == optionClasses[i]) { + E[] values = option.getEnumConstants(); + return values[combos[i].getSelectedIndex()]; + } + } + + return null; + } + + public boolean isOptionEnabled(Class option) { + for (int i = 0; i < optionClasses.length; i++) { + if (option == optionClasses[i]) { + return checkBoxes[i].isSelected(); + } + } + + return false; + } + + public double getZoom() { + return Double.parseDouble(zoomTextField.getText()) / 100; + } + + private void saveConfig() { + StringBuilder cfg = new StringBuilder(); + for (int i = 0; i < optionNames.length; i++) { + int selIndex = combos[i].getSelectedIndex(); + Class c = optionClasses[i]; + Object[] vals = c.getEnumConstants(); + String key = optionNames[i] + "." + vals[selIndex].toString().toLowerCase(); + if (i > 0) { + cfg.append(","); + } + cfg.append(key); + } + + Configuration.lastSelectedExportZoom.set(Double.parseDouble(zoomTextField.getText()) / 100); + Configuration.lastSelectedExportFormats.set(cfg.toString()); + } + + public ExportDialog(List exportables) { + setTitle(translate("dialog.title")); + setResizable(false); + + Container cnt = getContentPane(); + cnt.setLayout(new BorderLayout()); + JPanel comboPanel = new JPanel(null); + int labWidth = 0; + boolean[] exportableExistsArray = new boolean[optionNames.length]; + for (int i = 0; i < optionNames.length; i++) { + boolean exportableExists = false; + if (exportables == null) { + exportableExists = true; + } else { + for (TreeItem e : exportables) { + for (int j = 0; j < objClasses[i].length; j++) { + if (objClasses[i][j].isInstance(e)) { + exportableExists = true; + } + } + } + } + + if (!exportableExists) { + continue; + } + + exportableExistsArray[i] = true; + + JLabel label = new JLabel(translate(optionNames[i])); + if (label.getPreferredSize().width > labWidth) { + labWidth = label.getPreferredSize().width; + } + } + + String exportFormatsStr = Configuration.lastSelectedExportFormats.get(); + if ("".equals(exportFormatsStr)) { + exportFormatsStr = null; + } + + String[] exportFormatsArr = new String[0]; + if (exportFormatsStr != null) { + if (exportFormatsStr.contains(",")) { + exportFormatsArr = exportFormatsStr.split(","); + } else { + exportFormatsArr = new String[]{exportFormatsStr}; + } + + } + + int comboWidth = 200; + int checkBoxWidth; + int top = 10; + + List exportFormats = Arrays.asList(exportFormatsArr); + combos = new JComboBox[optionNames.length]; + checkBoxes = new JCheckBox[optionNames.length]; + selectAllCheckBox = new JCheckBox(); + checkBoxWidth = selectAllCheckBox.getPreferredSize().width; + selectAllCheckBox.setBounds(10 + labWidth + 10 + comboWidth + 10, top, checkBoxWidth, selectAllCheckBox.getPreferredSize().height); + selectAllCheckBox.setSelected(true); + selectAllCheckBox.addActionListener((ActionEvent e) -> { + boolean selected = selectAllCheckBox.isSelected(); + for (JCheckBox checkBox : checkBoxes) { + if (checkBox != null) { + checkBox.setSelected(selected); + } + } + }); + comboPanel.add(selectAllCheckBox); + top += selectAllCheckBox.getHeight(); + + boolean zoomable = false; + for (int i = 0; i < optionNames.length; i++) { + Class c = optionClasses[i]; + Object[] vals = c.getEnumConstants(); + String[] names = new String[vals.length]; + int itemIndex = -1; + for (int j = 0; j < vals.length; j++) { + + String key = optionNames[i] + "." + vals[j].toString().toLowerCase(); + if (exportFormats.contains(key)) { + itemIndex = j; + } + names[j] = translate(key); + } + + combos[i] = new JComboBox<>(names); + if (itemIndex > -1) { + combos[i].setSelectedIndex(itemIndex); + } + + combos[i].setBounds(10 + labWidth + 10, top, comboWidth, combos[i].getPreferredSize().height); + + checkBoxes[i] = new JCheckBox(); + checkBoxes[i].setBounds(10 + labWidth + 10 + comboWidth + 10, top, checkBoxWidth, checkBoxes[i].getPreferredSize().height); + checkBoxes[i].setSelected(true); + + if (!exportableExistsArray[i]) { + continue; + } + + if (Arrays.asList(zoomClasses).contains(c)) { + zoomable = true; + } + + JLabel lab = new JLabel(translate(optionNames[i])); + lab.setBounds(10, top, lab.getPreferredSize().width, lab.getPreferredSize().height); + comboPanel.add(lab); + comboPanel.add(checkBoxes[i]); + comboPanel.add(combos[i]); + top += combos[i].getHeight(); + } + + int zoomWidth = 50; + if (zoomable) { + top += 2; + JLabel zlab = new JLabel(translate("zoom")); + zlab.setBounds(10, top + 4, zlab.getPreferredSize().width, zlab.getPreferredSize().height); + zoomTextField.setBounds(10 + labWidth + 10, top, zoomWidth, zoomTextField.getPreferredSize().height); + JLabel pctLabel = new JLabel(translate("zoom.percent")); + pctLabel.setBounds(10 + labWidth + 10 + zoomWidth + 5, top + 4, 20, pctLabel.getPreferredSize().height); + + comboPanel.add(zlab); + comboPanel.add(zoomTextField); + comboPanel.add(pctLabel); + top += zoomTextField.getHeight(); + } + + Dimension dim = new Dimension(10 + labWidth + 10 + checkBoxWidth + 10 + comboWidth + 10, top + 10); + comboPanel.setMinimumSize(dim); + comboPanel.setPreferredSize(dim); + cnt.add(comboPanel, BorderLayout.CENTER); + + JPanel buttonsPanel = new JPanel(new FlowLayout()); + JButton okButton = new JButton(translate("button.ok")); + okButton.addActionListener(this::okButtonActionPerformed); + + JButton cancelButton = new JButton(translate("button.cancel")); + cancelButton.addActionListener(this::cancelButtonActionPerformed); + + buttonsPanel.add(okButton); + buttonsPanel.add(cancelButton); + + cnt.add(buttonsPanel, BorderLayout.SOUTH); + pack(); + View.centerScreen(this); + View.setWindowIcon(this); + getRootPane().setDefaultButton(okButton); + setModal(true); + String pct = "" + Configuration.lastSelectedExportZoom.get() * 100; + if (pct.endsWith(".0")) { + pct = pct.substring(0, pct.length() - 2); + } + + zoomTextField.setText(pct); + } + + @Override + public void setVisible(boolean b) { + if (b) { + result = ERROR_OPTION; + } + super.setVisible(b); + } + + private void okButtonActionPerformed(ActionEvent evt) { + result = OK_OPTION; + try { + saveConfig(); + } catch (NumberFormatException nfe) { + JOptionPane.showMessageDialog(ExportDialog.this, translate("zoom.invalid"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + zoomTextField.requestFocusInWindow(); + return; + } + + setVisible(false); + } + + private void cancelButtonActionPerformed(ActionEvent evt) { + result = CANCEL_OPTION; + setVisible(false); + } + + public int showExportDialog() { + setVisible(true); + return result; + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/FontEmbedDialog.java b/src/com/jpexs/decompiler/flash/gui/FontEmbedDialog.java index 5d34915e8..2579206b0 100644 --- a/src/com/jpexs/decompiler/flash/gui/FontEmbedDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/FontEmbedDialog.java @@ -1,401 +1,401 @@ -/* - * Copyright (C) 2010-2016 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.configuration.Configuration; -import com.jpexs.decompiler.flash.tags.font.CharacterRanges; -import com.jpexs.helpers.Helper; -import java.awt.BorderLayout; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.FontFormatException; -import java.awt.event.ActionEvent; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.io.File; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; -import java.util.TreeSet; -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.ButtonGroup; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JRadioButton; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.filechooser.FileFilter; - -/** - * - * @author JPEXS - */ -public class FontEmbedDialog extends AppDialog { - - private static final int SAMPLE_MAX_LENGTH = 50; - - private final JComboBox familyNamesSelection; - - private final JComboBox faceSelection; - - private final JCheckBox[] rangeCheckboxes; - - private final String rangeNames[]; - - private final JLabel[] rangeSamples; - - private final JTextField individualCharsField; - - private int result = ERROR_OPTION; - - private JLabel individialSample; - - private Font customFont; - - private final JCheckBox allCheckbox; - - public Font getSelectedFont() { - if (ttfFileRadio.isSelected() && customFont != null) { - return customFont; - } - return ((FontFace) faceSelection.getSelectedItem()).font; - } - - public Set getSelectedChars() { - Set chars = new TreeSet<>(); - Font f = getSelectedFont(); - if (allCheckbox.isSelected()) { - for (int i = 0; i < rangeCheckboxes.length; i++) { - int[] codes = CharacterRanges.rangeCodes(i); - for (int c : codes) { - if (f.canDisplay(c)) { - chars.add(c); - } - } - } - } else { - for (int i = 0; i < rangeCheckboxes.length; i++) { - if (rangeCheckboxes[i].isSelected()) { - int[] codes = CharacterRanges.rangeCodes(i); - for (int c : codes) { - if (f.canDisplay(c)) { - chars.add(c); - } - } - } - } - String indStr = individualCharsField.getText(); - for (int i = 0; i < indStr.length(); i++) { - if (f.canDisplay(indStr.codePointAt(i))) { - chars.add(indStr.codePointAt(i)); - } - } - } - return chars; - } - - private JRadioButton ttfFileRadio; - - private JRadioButton installedRadio; - - private void updateFaceSelection() { - faceSelection.setModel(FontPanel.getFaceModel((FontFamily) familyNamesSelection.getSelectedItem())); - } - - public FontEmbedDialog(FontFace selectedFace, String selectedChars) { - setSize(900, 600); - setDefaultCloseOperation(HIDE_ON_CLOSE); - setTitle(translate("dialog.title")); - - Container cnt = getContentPane(); - cnt.setLayout(new BoxLayout(cnt, BoxLayout.Y_AXIS)); - - JPanel selFontPanel = new JPanel(new FlowLayout()); - - installedRadio = new JRadioButton(translate("installed")); - ttfFileRadio = new JRadioButton(translate("ttffile.noselection")); - - ButtonGroup bg = new ButtonGroup(); - bg.add(installedRadio); - bg.add(ttfFileRadio); - - installedRadio.setSelected(true); - - individialSample = new JLabel(); - familyNamesSelection = new JComboBox<>(FontPanel.getFamilyModel()); - familyNamesSelection.setSelectedItem(new FontFamily(selectedFace.font)); - faceSelection = new JComboBox<>(); - updateFaceSelection(); - faceSelection.setSelectedItem(selectedFace); - JButton loadFromDiskButton = new JButton(View.getIcon("open16")); - loadFromDiskButton.setToolTipText(translate("button.loadfont")); - loadFromDiskButton.addActionListener(this::loadFromDiscButtonActionPerformed); - selFontPanel.add(installedRadio); - selFontPanel.add(familyNamesSelection); - selFontPanel.add(faceSelection); - selFontPanel.add(ttfFileRadio); - selFontPanel.add(loadFromDiskButton); - - installedRadio.addItemListener(new ItemListener() { - - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() == ItemEvent.SELECTED) { - updateCheckboxes(); - } - } - }); - - ttfFileRadio.addItemListener(new ItemListener() { - - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() == ItemEvent.SELECTED) { - if (ttfFileRadio.isSelected()) { - if (customFont == null) { - if (loadFromDisk()) { - updateCheckboxes(); - } else { - installedRadio.setSelected(true); - } - } else { - updateCheckboxes(); - } - } - } - } - }); - - cnt.add(selFontPanel); - JPanel rangesPanel = new JPanel(); - rangesPanel.setLayout(new BoxLayout(rangesPanel, BoxLayout.Y_AXIS)); - final int rc = CharacterRanges.rangeCount(); - rangeCheckboxes = new JCheckBox[rc]; - rangeSamples = new JLabel[rc]; - rangeNames = new String[rc]; - allCheckbox = new JCheckBox(translate("allcharacters")); - allCheckbox.addItemListener(new ItemListener() { - - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() == ItemEvent.SELECTED) { - for (int i = 0; i < rc; i++) { - rangeCheckboxes[i].setEnabled(false); - } - individualCharsField.setEnabled(false); - } else if (e.getStateChange() == ItemEvent.DESELECTED) { - for (int i = 0; i < rc; i++) { - rangeCheckboxes[i].setEnabled(true); - } - individualCharsField.setEnabled(true); - } - } - }); - JPanel rangeRowPanel = new JPanel(); - rangeRowPanel.setLayout(new BorderLayout()); - rangeRowPanel.add(allCheckbox, BorderLayout.WEST); - rangeRowPanel.setAlignmentX(0); - rangesPanel.add(rangeRowPanel); - - for (int i = 0; i < rc; i++) { - rangeNames[i] = CharacterRanges.rangeName(i); - rangeSamples[i] = new JLabel(""); - rangeCheckboxes[i] = new JCheckBox(rangeNames[i]); - rangeRowPanel = new JPanel(); - rangeRowPanel.setLayout(new BoxLayout(rangeRowPanel, BoxLayout.X_AXIS)); - rangeRowPanel.add(rangeCheckboxes[i]); - rangeRowPanel.add(Box.createHorizontalGlue()); - rangeRowPanel.add(rangeSamples[i]); - rangeRowPanel.setAlignmentX(0); - rangesPanel.add(rangeRowPanel); - } - cnt.add(new JScrollPane(rangesPanel)); - - JPanel specialPanel = new JPanel(); - specialPanel.setLayout(new BoxLayout(specialPanel, BoxLayout.X_AXIS)); - specialPanel.add(new JLabel(translate("label.individual"))); - individualCharsField = new JTextField(); - individualCharsField.setPreferredSize(new Dimension(100, individualCharsField.getPreferredSize().height)); - individialSample = new JLabel(); - specialPanel.add(individualCharsField); - - cnt.add(specialPanel); - cnt.add(individialSample); - - JPanel buttonsPanel = new JPanel(new FlowLayout()); - JButton okButton = new JButton(AppStrings.translate("button.ok")); - okButton.addActionListener(this::okButtonActionPerformed); - JButton cancelButton = new JButton(AppStrings.translate("button.cancel")); - cancelButton.addActionListener(this::cancelButtonActionPerformed); - buttonsPanel.add(okButton); - buttonsPanel.add(cancelButton); - cnt.add(buttonsPanel); - View.setWindowIcon(this); - View.centerScreen(this); - setModalityType(ModalityType.APPLICATION_MODAL); - individualCharsField.setText(selectedChars); - getRootPane().setDefaultButton(okButton); - familyNamesSelection.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - updateFaceSelection(); - updateCheckboxes(); - } - }); - faceSelection.addItemListener((ItemEvent e) -> { - updateCheckboxes(); - }); - updateCheckboxes(); - individualCharsField.getDocument().addDocumentListener(new DocumentListener() { - - @Override - public void insertUpdate(DocumentEvent e) { - updateIndividual(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - updateIndividual(); - } - - @Override - public void changedUpdate(DocumentEvent e) { - updateIndividual(); - } - }); - } - - private void updateIndividual() { - String chars = individualCharsField.getText(); - Font f = getSelectedFont(); - StringBuilder visibleChars = new StringBuilder(); - for (int i = 0; i < chars.length(); i++) { - if (f.canDisplay(chars.codePointAt(i))) { - visibleChars.append(chars.charAt(i)); - } - } - individialSample.setText(visibleChars.toString()); - } - - private void updateCheckboxes() { - Font f = getSelectedFont().deriveFont(12f); - int rc = CharacterRanges.rangeCount(); - - Set allChars = new HashSet<>(); - for (int i = 0; i < rc; i++) { - rangeNames[i] = CharacterRanges.rangeName(i); - int[] codes = CharacterRanges.rangeCodes(i); - int avail = 0; - StringBuilder sample = new StringBuilder(); - for (int c = 0; c < codes.length; c++) { - if (f.canDisplay(codes[c])) { - allChars.add(codes[c]); - if (avail < SAMPLE_MAX_LENGTH) { - sample.append((char) codes[c]); - } - avail++; - } - } - rangeSamples[i].setText(sample.toString()); - rangeSamples[i].setFont(f); - rangeCheckboxes[i].setText(translate("range.description").replace("%available%", Integer.toString(avail)).replace("%name%", rangeNames[i]).replace("%total%", Integer.toString(codes.length))); - } - allCheckbox.setText(translate("allcharacters").replace("%available%", Integer.toString(allChars.size()))); - individialSample.setFont(f); - updateIndividual(); - } - - @Override - public void setVisible(boolean b) { - if (b) { - result = ERROR_OPTION; - } - - super.setVisible(b); - } - - private void okButtonActionPerformed(ActionEvent evt) { - result = OK_OPTION; - setVisible(false); - } - - private void cancelButtonActionPerformed(ActionEvent evt) { - result = CANCEL_OPTION; - setVisible(false); - } - - private void loadFromDiscButtonActionPerformed(ActionEvent evt) { - if (customFont != null) { - if (loadFromDisk()) { - updateCheckboxes(); - } - } - - ttfFileRadio.setSelected(true); - } - - private boolean loadFromDisk() { - JFileChooser fc = new JFileChooser(); - fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); - FileFilter ttfFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".ttf")) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return translate("filter.ttf"); - } - }; - fc.setFileFilter(ttfFilter); - fc.setAcceptAllFileFilterUsed(true); - JFrame f = new JFrame(); - View.setWindowIcon(f); - int returnVal = fc.showOpenDialog(f); - if (returnVal == JFileChooser.APPROVE_OPTION) { - Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); - File selfile = Helper.fixDialogFile(fc.getSelectedFile()); - try { - customFont = Font.createFont(Font.TRUETYPE_FONT, selfile); - ttfFileRadio.setText(translate("ttffile.selection").replace("%fontname%", customFont.getName()).replace("%filename%", selfile.getName())); - return true; - } catch (FontFormatException ex) { - JOptionPane.showMessageDialog(this, translate("error.invalidfontfile"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } catch (IOException ex) { - JOptionPane.showMessageDialog(this, translate("error.cannotreadfontfile"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - } - return false; - } - - public int showDialog() { - setVisible(true); - return result; - } -} +/* + * Copyright (C) 2010-2016 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.configuration.Configuration; +import com.jpexs.decompiler.flash.tags.font.CharacterRanges; +import com.jpexs.helpers.Helper; +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.FontFormatException; +import java.awt.event.ActionEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.filechooser.FileFilter; + +/** + * + * @author JPEXS + */ +public class FontEmbedDialog extends AppDialog { + + private static final int SAMPLE_MAX_LENGTH = 50; + + private final JComboBox familyNamesSelection; + + private final JComboBox faceSelection; + + private final JCheckBox[] rangeCheckboxes; + + private final String rangeNames[]; + + private final JLabel[] rangeSamples; + + private final JTextField individualCharsField; + + private int result = ERROR_OPTION; + + private JLabel individialSample; + + private Font customFont; + + private final JCheckBox allCheckbox; + + public Font getSelectedFont() { + if (ttfFileRadio.isSelected() && customFont != null) { + return customFont; + } + return ((FontFace) faceSelection.getSelectedItem()).font; + } + + public Set getSelectedChars() { + Set chars = new TreeSet<>(); + Font f = getSelectedFont(); + if (allCheckbox.isSelected()) { + for (int i = 0; i < rangeCheckboxes.length; i++) { + int[] codes = CharacterRanges.rangeCodes(i); + for (int c : codes) { + if (f.canDisplay(c)) { + chars.add(c); + } + } + } + } else { + for (int i = 0; i < rangeCheckboxes.length; i++) { + if (rangeCheckboxes[i].isSelected()) { + int[] codes = CharacterRanges.rangeCodes(i); + for (int c : codes) { + if (f.canDisplay(c)) { + chars.add(c); + } + } + } + } + String indStr = individualCharsField.getText(); + for (int i = 0; i < indStr.length(); i++) { + if (f.canDisplay(indStr.codePointAt(i))) { + chars.add(indStr.codePointAt(i)); + } + } + } + return chars; + } + + private JRadioButton ttfFileRadio; + + private JRadioButton installedRadio; + + private void updateFaceSelection() { + faceSelection.setModel(FontPanel.getFaceModel((FontFamily) familyNamesSelection.getSelectedItem())); + } + + public FontEmbedDialog(FontFace selectedFace, String selectedChars) { + setSize(900, 600); + setDefaultCloseOperation(HIDE_ON_CLOSE); + setTitle(translate("dialog.title")); + + Container cnt = getContentPane(); + cnt.setLayout(new BoxLayout(cnt, BoxLayout.Y_AXIS)); + + JPanel selFontPanel = new JPanel(new FlowLayout()); + + installedRadio = new JRadioButton(translate("installed")); + ttfFileRadio = new JRadioButton(translate("ttffile.noselection")); + + ButtonGroup bg = new ButtonGroup(); + bg.add(installedRadio); + bg.add(ttfFileRadio); + + installedRadio.setSelected(true); + + individialSample = new JLabel(); + familyNamesSelection = new JComboBox<>(FontPanel.getFamilyModel()); + familyNamesSelection.setSelectedItem(new FontFamily(selectedFace.font)); + faceSelection = new JComboBox<>(); + updateFaceSelection(); + faceSelection.setSelectedItem(selectedFace); + JButton loadFromDiskButton = new JButton(View.getIcon("open16")); + loadFromDiskButton.setToolTipText(translate("button.loadfont")); + loadFromDiskButton.addActionListener(this::loadFromDiscButtonActionPerformed); + selFontPanel.add(installedRadio); + selFontPanel.add(familyNamesSelection); + selFontPanel.add(faceSelection); + selFontPanel.add(ttfFileRadio); + selFontPanel.add(loadFromDiskButton); + + installedRadio.addItemListener(new ItemListener() { + + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + updateCheckboxes(); + } + } + }); + + ttfFileRadio.addItemListener(new ItemListener() { + + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + if (ttfFileRadio.isSelected()) { + if (customFont == null) { + if (loadFromDisk()) { + updateCheckboxes(); + } else { + installedRadio.setSelected(true); + } + } else { + updateCheckboxes(); + } + } + } + } + }); + + cnt.add(selFontPanel); + JPanel rangesPanel = new JPanel(); + rangesPanel.setLayout(new BoxLayout(rangesPanel, BoxLayout.Y_AXIS)); + final int rc = CharacterRanges.rangeCount(); + rangeCheckboxes = new JCheckBox[rc]; + rangeSamples = new JLabel[rc]; + rangeNames = new String[rc]; + allCheckbox = new JCheckBox(translate("allcharacters")); + allCheckbox.addItemListener(new ItemListener() { + + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + for (int i = 0; i < rc; i++) { + rangeCheckboxes[i].setEnabled(false); + } + individualCharsField.setEnabled(false); + } else if (e.getStateChange() == ItemEvent.DESELECTED) { + for (int i = 0; i < rc; i++) { + rangeCheckboxes[i].setEnabled(true); + } + individualCharsField.setEnabled(true); + } + } + }); + JPanel rangeRowPanel = new JPanel(); + rangeRowPanel.setLayout(new BorderLayout()); + rangeRowPanel.add(allCheckbox, BorderLayout.WEST); + rangeRowPanel.setAlignmentX(0); + rangesPanel.add(rangeRowPanel); + + for (int i = 0; i < rc; i++) { + rangeNames[i] = CharacterRanges.rangeName(i); + rangeSamples[i] = new JLabel(""); + rangeCheckboxes[i] = new JCheckBox(rangeNames[i]); + rangeRowPanel = new JPanel(); + rangeRowPanel.setLayout(new BoxLayout(rangeRowPanel, BoxLayout.X_AXIS)); + rangeRowPanel.add(rangeCheckboxes[i]); + rangeRowPanel.add(Box.createHorizontalGlue()); + rangeRowPanel.add(rangeSamples[i]); + rangeRowPanel.setAlignmentX(0); + rangesPanel.add(rangeRowPanel); + } + cnt.add(new JScrollPane(rangesPanel)); + + JPanel specialPanel = new JPanel(); + specialPanel.setLayout(new BoxLayout(specialPanel, BoxLayout.X_AXIS)); + specialPanel.add(new JLabel(translate("label.individual"))); + individualCharsField = new JTextField(); + individualCharsField.setPreferredSize(new Dimension(100, individualCharsField.getPreferredSize().height)); + individialSample = new JLabel(); + specialPanel.add(individualCharsField); + + cnt.add(specialPanel); + cnt.add(individialSample); + + JPanel buttonsPanel = new JPanel(new FlowLayout()); + JButton okButton = new JButton(AppStrings.translate("button.ok")); + okButton.addActionListener(this::okButtonActionPerformed); + JButton cancelButton = new JButton(AppStrings.translate("button.cancel")); + cancelButton.addActionListener(this::cancelButtonActionPerformed); + buttonsPanel.add(okButton); + buttonsPanel.add(cancelButton); + cnt.add(buttonsPanel); + View.setWindowIcon(this); + View.centerScreen(this); + setModalityType(ModalityType.APPLICATION_MODAL); + individualCharsField.setText(selectedChars); + getRootPane().setDefaultButton(okButton); + familyNamesSelection.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + updateFaceSelection(); + updateCheckboxes(); + } + }); + faceSelection.addItemListener((ItemEvent e) -> { + updateCheckboxes(); + }); + updateCheckboxes(); + individualCharsField.getDocument().addDocumentListener(new DocumentListener() { + + @Override + public void insertUpdate(DocumentEvent e) { + updateIndividual(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + updateIndividual(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + updateIndividual(); + } + }); + } + + private void updateIndividual() { + String chars = individualCharsField.getText(); + Font f = getSelectedFont(); + StringBuilder visibleChars = new StringBuilder(); + for (int i = 0; i < chars.length(); i++) { + if (f.canDisplay(chars.codePointAt(i))) { + visibleChars.append(chars.charAt(i)); + } + } + individialSample.setText(visibleChars.toString()); + } + + private void updateCheckboxes() { + Font f = getSelectedFont().deriveFont(12f); + int rc = CharacterRanges.rangeCount(); + + Set allChars = new HashSet<>(); + for (int i = 0; i < rc; i++) { + rangeNames[i] = CharacterRanges.rangeName(i); + int[] codes = CharacterRanges.rangeCodes(i); + int avail = 0; + StringBuilder sample = new StringBuilder(); + for (int c = 0; c < codes.length; c++) { + if (f.canDisplay(codes[c])) { + allChars.add(codes[c]); + if (avail < SAMPLE_MAX_LENGTH) { + sample.append((char) codes[c]); + } + avail++; + } + } + rangeSamples[i].setText(sample.toString()); + rangeSamples[i].setFont(f); + rangeCheckboxes[i].setText(translate("range.description").replace("%available%", Integer.toString(avail)).replace("%name%", rangeNames[i]).replace("%total%", Integer.toString(codes.length))); + } + allCheckbox.setText(translate("allcharacters").replace("%available%", Integer.toString(allChars.size()))); + individialSample.setFont(f); + updateIndividual(); + } + + @Override + public void setVisible(boolean b) { + if (b) { + result = ERROR_OPTION; + } + + super.setVisible(b); + } + + private void okButtonActionPerformed(ActionEvent evt) { + result = OK_OPTION; + setVisible(false); + } + + private void cancelButtonActionPerformed(ActionEvent evt) { + result = CANCEL_OPTION; + setVisible(false); + } + + private void loadFromDiscButtonActionPerformed(ActionEvent evt) { + if (customFont != null) { + if (loadFromDisk()) { + updateCheckboxes(); + } + } + + ttfFileRadio.setSelected(true); + } + + private boolean loadFromDisk() { + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); + FileFilter ttfFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".ttf")) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return translate("filter.ttf"); + } + }; + fc.setFileFilter(ttfFilter); + fc.setAcceptAllFileFilterUsed(true); + JFrame f = new JFrame(); + View.setWindowIcon(f); + int returnVal = fc.showOpenDialog(f); + if (returnVal == JFileChooser.APPROVE_OPTION) { + Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); + File selfile = Helper.fixDialogFile(fc.getSelectedFile()); + try { + customFont = Font.createFont(Font.TRUETYPE_FONT, selfile); + ttfFileRadio.setText(translate("ttffile.selection").replace("%fontname%", customFont.getName()).replace("%filename%", selfile.getName())); + return true; + } catch (FontFormatException ex) { + JOptionPane.showMessageDialog(this, translate("error.invalidfontfile"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } catch (IOException ex) { + JOptionPane.showMessageDialog(this, translate("error.cannotreadfontfile"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + } + return false; + } + + public int showDialog() { + setVisible(true); + return result; + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index 9694928c6..c554c43b1 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -1,2335 +1,2335 @@ -/* - * Copyright (C) 2010-2016 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.debugger.flash.Debugger; -import com.jpexs.debugger.flash.DebuggerCommands; -import com.jpexs.debugger.flash.Variable; -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.SearchMode; -import com.jpexs.decompiler.flash.SwfOpenException; -import com.jpexs.decompiler.flash.UrlResolver; -import com.jpexs.decompiler.flash.Version; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.configuration.SwfSpecificConfiguration; -import com.jpexs.decompiler.flash.console.CommandLineArgumentParser; -import com.jpexs.decompiler.flash.console.ContextMenuTools; -import com.jpexs.decompiler.flash.exporters.modes.ExeExportMode; -import com.jpexs.decompiler.flash.gui.debugger.DebugListener; -import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools; -import com.jpexs.decompiler.flash.gui.pipes.FirstInstance; -import com.jpexs.decompiler.flash.gui.proxy.ProxyFrame; -import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.ImportTag; -import com.jpexs.decompiler.flash.treeitems.SWFList; -import com.jpexs.helpers.Cache; -import com.jpexs.helpers.CancellableWorker; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.Path; -import com.jpexs.helpers.ProgressListener; -import com.jpexs.helpers.Stopwatch; -import com.jpexs.helpers.streams.SeekableInputStream; -import com.sun.jna.Platform; -import com.sun.jna.platform.win32.Advapi32Util; -import com.sun.jna.platform.win32.Kernel32; -import com.sun.jna.platform.win32.WinReg; -import java.awt.AWTException; -import java.awt.Frame; -import java.awt.GraphicsEnvironment; -import java.awt.MenuItem; -import java.awt.PopupMenu; -import java.awt.SystemTray; -import java.awt.TrayIcon; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.lang.reflect.Field; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.net.URL; -import java.net.URLConnection; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.logging.ConsoleHandler; -import java.util.logging.FileHandler; -import java.util.logging.Formatter; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.logging.SimpleFormatter; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JOptionPane; -import javax.swing.SwingWorker; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; -import javax.swing.filechooser.FileFilter; -import org.pushingpixels.substance.api.SubstanceLookAndFeel; - -/** - * Main executable class - * - * @author JPEXS - */ -public class Main { - - protected static ProxyFrame proxyFrame; - - private static List sourceInfos = new ArrayList<>(); - - public static LoadingDialog loadingDialog; - - private static boolean working = false; - - private static TrayIcon trayIcon; - - private static MenuItem stopMenuItem; - - private static volatile MainFrame mainFrame; - - public static final int UPDATE_SYSTEM_MAJOR = 1; - - public static final int UPDATE_SYSTEM_MINOR = 3; - - private static LoadFromMemoryFrame loadFromMemoryFrame; - - private static LoadFromCacheFrame loadFromCacheFrame; - - private static final Logger logger = Logger.getLogger(Main.class.getName()); - - public static DebugLogDialog debugDialog; - - public static boolean shouldCloseWhenClosingLoadingDialog; - - private static Debugger flashDebugger; - - private static DebuggerHandler debugHandler = null; - - //private static int ip = 0; - //private static String ipClass = null; - private static Process runProcess; - - private static boolean runProcessDebug; - - private static boolean runProcessDebugPCode; - - private static boolean inited = false; - - private static File runTempFile; - - private static List runTempFiles = new ArrayList<>(); - - public static void freeRun() { - synchronized (Main.class) { - if (runTempFile != null) { - runTempFile.delete(); - runTempFile = null; - } - for (File f : runTempFiles) { - f.delete(); - } - runTempFiles.clear(); - - runProcess = null; - } - if (mainFrame != null && mainFrame.getPanel() != null) { - mainFrame.getPanel().clearDebuggerColors(); - } - if (runProcessDebug) { - Main.getDebugHandler().disconnect(); - } - } - - public static synchronized boolean isDebugPaused() { - return runProcess != null && runProcessDebug && getDebugHandler().isPaused(); - } - - public static synchronized boolean isDebugRunning() { - return runProcess != null && runProcessDebug; - } - - public static synchronized boolean isDebugPCode() { - return runProcessDebugPCode; - } - - public static synchronized boolean isDebugConnected() { - return getDebugHandler().isConnected(); - } - - public static synchronized boolean isRunning() { - return runProcess != null && !runProcessDebug; - } - - public static synchronized boolean addWatch(Variable v, long v_id, boolean watchRead, boolean watchWrite) { - DebuggerCommands.Watch w = getDebugHandler().addWatch(v, v_id, watchRead, watchWrite); - return w != null; - } - - public static void runPlayer(String title, final String exePath, String file, String flashVars) { - if (!new File(file).exists()) { - return; - } - if (flashVars != null && !flashVars.isEmpty()) { - file += "?" + flashVars; - } - final String ffile = file; - - CancellableWorker runWorker = new CancellableWorker() { - @Override - protected Object doInBackground() throws Exception { - Process proc; - try { - proc = Runtime.getRuntime().exec(new String[]{exePath, ffile}); - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - return null; - } - boolean isDebug; - - synchronized (Main.class) { - runProcess = proc; - isDebug = runProcessDebug; - } - if (isDebug) { - mainFrame.getMenu().hilightPath("/debugging"); - } - mainFrame.getMenu().updateComponents(); - try { - if (proc != null) { - proc.waitFor(); - } - } catch (InterruptedException ex) { - if (proc != null) { - try { - proc.destroy(); - } catch (Exception ex2) { - //ignore - } - } - } - freeRun(); - stopDebugger(); - mainFrame.getMenu().updateComponents(); - return null; - } - - @Override - protected void done() { - Main.stopWork(); - } - - @Override - public void workerCancelled() { - Main.stopWork(); - synchronized (Main.class) { - if (runProcess != null) { - try { - runProcess.destroy(); - } catch (Exception ex) { - - } - } - } - freeRun(); - mainFrame.getMenu().updateComponents(); - } - }; - - mainFrame.getMenu().updateComponents(); - Main.startWork(title + "...", runWorker); - runWorker.execute(); - } - - public static void stopRun() { - - synchronized (Main.class) { - if (runProcess != null) { - runProcess.destroy(); - } - } - freeRun(); - stopDebugger(); - mainFrame.getMenu().updateComponents(); - } - - private static interface SwfPreparation { - - public SWF prepare(SWF swf); - } - - private static class SwfRunPrepare implements SwfPreparation { - - @Override - public SWF prepare(SWF swf) { - if (Configuration.autoOpenLoadedSWFs.get()) { - if (!DebuggerTools.hasDebugger(swf)) { - DebuggerTools.switchDebugger(swf); - } - DebuggerTools.injectDebugLoader(swf); - } - return swf; - } - } - - private static class SwfDebugPrepare extends SwfRunPrepare { - - private boolean doPCode; - - public SwfDebugPrepare(boolean doPCode) { - this.doPCode = doPCode; - } - - @Override - public SWF prepare(SWF instrSWF) { - instrSWF = super.prepare(instrSWF); - try { - File fTempFile = new File(instrSWF.getFile()); - instrSWF.enableDebugging(true, new File("."), true, doPCode); - FileOutputStream fos = new FileOutputStream(fTempFile); - instrSWF.saveTo(fos); - fos.close(); - if (!instrSWF.isAS3()) { - //Read again, because line file offsets changed with adding debug tags - //TODO: handle somehow without rereading? - instrSWF = null; - try (FileInputStream fis = new FileInputStream(fTempFile)) { - instrSWF = new SWF(fis, false, false); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - if (instrSWF != null) { - String swfFileName = fTempFile.getAbsolutePath(); - if (swfFileName.toLowerCase().endsWith(".swf")) { - swfFileName = swfFileName.substring(0, swfFileName.length() - 4) + ".swd"; - } else { - swfFileName = swfFileName + ".swd"; - } - File swdFile = new File(swfFileName); - if (doPCode) { - instrSWF.generatePCodeSwdFile(swdFile, getPackBreakPoints(true)); - } else { - instrSWF.generateSwdFile(swdFile, getPackBreakPoints(true)); - } - } - } - } catch (IOException ex) { - //ignore, return instrSWF - } - return instrSWF; - } - } - - private static void prepareSwf(SwfPreparation prep, File toPrepareFile, File origFile, List tempFiles) throws IOException { - SWF instrSWF = null; - try (FileInputStream fis = new FileInputStream(toPrepareFile)) { - instrSWF = new SWF(fis, toPrepareFile.getAbsolutePath(), origFile.getName(), false); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - if (instrSWF != null) { - for (Tag t : instrSWF.getLocalTags()) { - if (t instanceof ImportTag) { - ImportTag it = (ImportTag) t; - String url = it.getUrl(); - File importedFile = new File(origFile.getParentFile(), url); - if (importedFile.exists()) { - File newTempFile = File.createTempFile("ffdec_run_import_", ".swf"); - it.setUrl("./" + newTempFile.getName()); - byte[] impData = Helper.readFile(importedFile.getAbsolutePath()); - Helper.writeFile(newTempFile.getAbsolutePath(), impData); - tempFiles.add(newTempFile); - prepareSwf(prep, newTempFile, importedFile, tempFiles); - } - } - } - if (prep != null) { - instrSWF = prep.prepare(instrSWF); - } - try (FileOutputStream fos = new FileOutputStream(toPrepareFile)) { - instrSWF.saveTo(fos); - } - } - } - - public static void run(SWF swf) { - String flashVars = "";//key=val&key2=val2 - String playerLocation = Configuration.playerLocation.get(); - if (playerLocation.isEmpty() || (!new File(playerLocation).exists())) { - View.showMessageDialog(null, AppStrings.translate("message.playerpath.notset"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - advancedSettings("paths"); - return; - } - if (swf == null) { - return; - } - File tempFile; - List tempFiles = new ArrayList<>(); - try { - tempFile = File.createTempFile("ffdec_run_", ".swf"); - - try (FileOutputStream fos = new FileOutputStream(tempFile)) { - swf.saveTo(fos); - } - - prepareSwf(new SwfRunPrepare(), tempFile, new File(swf.getFile()), tempFiles); - - } catch (IOException ex) { - return; - - } - if (tempFile != null) { - synchronized (Main.class) { - runTempFile = tempFile; - runTempFiles = tempFiles; - runProcessDebug = false; - } - runPlayer(AppStrings.translate("work.running"), playerLocation, tempFile.getAbsolutePath(), flashVars); - } - } - - public static void runDebug(SWF swf, final boolean doPCode) { - String flashVars = "";//key=val&key2=val2 - String playerLocation = Configuration.playerDebugLocation.get(); - if (playerLocation.isEmpty() || (!new File(playerLocation).exists())) { - View.showMessageDialog(null, AppStrings.translate("message.playerpath.debug.notset"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - Main.advancedSettings("paths"); - return; - } - if (swf == null) { - return; - } - File tempFile = null; - - try { - tempFile = File.createTempFile("ffdec_debug_", ".swf"); - } catch (Exception ex) { - - } - - if (tempFile != null) { - final File fTempFile = tempFile; - final List tempFiles = new ArrayList<>(); - CancellableWorker instrumentWorker = new CancellableWorker() { - @Override - protected Object doInBackground() throws Exception { - - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(fTempFile))) { - swf.saveTo(fos); - } - prepareSwf(new SwfDebugPrepare(doPCode), fTempFile, new File(swf.getFile()), tempFiles); - return null; - } - - @Override - public void workerCancelled() { - Main.stopWork(); - } - - @Override - protected void done() { - synchronized (Main.class) { - runTempFile = fTempFile; - runProcessDebug = true; - runProcessDebugPCode = doPCode; - runTempFiles = tempFiles; - } - Main.stopWork(); - Main.startDebugger(); - runPlayer(AppStrings.translate("work.debugging.wait"), playerLocation, fTempFile.getAbsolutePath(), flashVars); - } - }; - - Main.startWork(AppStrings.translate("work.debugging.instrumenting"), instrumentWorker); - instrumentWorker.execute(); - } - } - - /* public static void debuggerNotSuspended() { - - }*/ - public static boolean isDebugging() { - return isDebugRunning(); - } - - public synchronized static int getIp(Object pack) { - return getDebugHandler().getBreakIp(); - } - - public synchronized static String getIpClass() { - return getDebugHandler().getBreakScriptName(); - } - - public static synchronized boolean isBreakPointValid(String scriptName, int line) { - return !getDebugHandler().isBreakpointInvalid(scriptName, line); - } - - public synchronized static void addBreakPoint(String scriptName, int line) { - getDebugHandler().addBreakPoint(scriptName, line); - } - - public synchronized static void removeBreakPoint(String scriptName, int line) { - getDebugHandler().removeBreakPoint(scriptName, line); - } - - public synchronized static boolean toggleBreakPoint(String scriptName, int line) { - if (getDebugHandler().isBreakpointToAdd(scriptName, line) || getDebugHandler().isBreakpointConfirmed(scriptName, line) || getDebugHandler().isBreakpointInvalid(scriptName, line)) { - getDebugHandler().removeBreakPoint(scriptName, line); - return false; - } else { - getDebugHandler().addBreakPoint(scriptName, line); - return true; - } - } - - public synchronized static Map> getPackBreakPoints(boolean validOnly) { - return getDebugHandler().getAllBreakPoints(validOnly); - } - - public synchronized static Set getScriptBreakPoints(String pack, boolean onlyValid) { - return getDebugHandler().getBreakPoints(pack, onlyValid); - } - - public static DebuggerHandler getDebugHandler() { - return debugHandler; - } - - public static void ensureMainFrame() { - if (mainFrame == null) { - synchronized (Main.class) { - if (mainFrame == null) { - MainFrame frame; - if (Configuration.useRibbonInterface.get()) { - frame = new MainFrameRibbon(); - } else { - frame = new MainFrameClassic(); - } - frame.getPanel().setErrorState(ErrorLogFrame.getInstance().getErrorState()); - mainFrame = frame; - } - } - } - } - - public static MainFrame getMainFrame() { - return mainFrame; - } - - public static void loadFromCache() { - if (loadFromCacheFrame == null) { - loadFromCacheFrame = new LoadFromCacheFrame(); - } - loadFromCacheFrame.setVisible(true); - } - - public static void loadFromMemory() { - if (loadFromMemoryFrame == null) { - loadFromMemoryFrame = new LoadFromMemoryFrame(mainFrame); - } - loadFromMemoryFrame.setVisible(true); - } - - public static void setVariable(long parentId, String varName, int valueType, Object value) { - getDebugHandler().setVariable(parentId, varName, valueType, value); - } - - public static void setSubLimiter(boolean value) { - if (value) { - AVM2Code.toSourceLimit = Configuration.sublimiter.get(); - } else { - AVM2Code.toSourceLimit = -1; - } - } - - public synchronized static boolean isInited() { - return inited; - } - - public synchronized static void setSessionLoaded(boolean v) { - inited = v; - } - - public static boolean isWorking() { - return working; - } - - public static void startProxy(int port) { - if (proxyFrame == null) { - proxyFrame = new ProxyFrame(mainFrame); - } - - proxyFrame.setPort(port); - addTrayIcon(); - switchProxy(); - } - - public static void showProxy() { - if (proxyFrame == null) { - proxyFrame = new ProxyFrame(mainFrame); - } - proxyFrame.setVisible(true); - proxyFrame.setState(Frame.NORMAL); - } - - public static void startWork(String name, CancellableWorker worker) { - startWork(name, -1, worker); - } - - public static void startWork(final String name, final int percent, final CancellableWorker worker) { - working = true; - View.execInEventDispatchLater(() -> { - if (mainFrame != null) { - mainFrame.getPanel().setWorkStatus(name, worker); - if (percent == -1) { - mainFrame.getPanel().hidePercent(); - } else { - mainFrame.getPanel().setPercent(percent); - } - } - if (loadingDialog != null) { - loadingDialog.setDetail(name); - loadingDialog.setPercent(percent); - } - if (CommandLineArgumentParser.isCommandLineMode()) { - System.out.println(name); - } - }); - } - - public static void stopWork() { - working = false; - View.execInEventDispatchLater(() -> { - if (mainFrame != null) { - mainFrame.getPanel().setWorkStatus("", null); - } - if (loadingDialog != null) { - loadingDialog.setDetail(""); - } - }); - } - - public static SWFList parseSWF(SWFSourceInfo sourceInfo) throws Exception { - SWFList result = new SWFList(); - - InputStream inputStream = sourceInfo.getInputStream(); - SWFBundle bundle = null; - FileInputStream fis = null; - if (inputStream == null) { - inputStream = new BufferedInputStream(fis = new FileInputStream(sourceInfo.getFile())); - bundle = sourceInfo.getBundle(false, SearchMode.ALL); - logger.log(Level.INFO, "Load file: {0}", sourceInfo.getFile()); - } else if (inputStream instanceof SeekableInputStream - || inputStream instanceof BufferedInputStream) { - try { - inputStream.reset(); - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - logger.log(Level.INFO, "Load stream: {0}", sourceInfo.getFileTitle()); - } - - Stopwatch sw = Stopwatch.startNew(); - if (bundle != null) { - result.bundle = bundle; - result.name = new File(sourceInfo.getFileTitleOrName()).getName(); - for (Entry streamEntry : bundle.getAll().entrySet()) { - InputStream stream = streamEntry.getValue(); - stream.reset(); - CancellableWorker worker = new CancellableWorker() { - @Override - public SWF doInBackground() throws Exception { - final CancellableWorker worker = this; - SWF swf = new SWF(stream, null, streamEntry.getKey(), new ProgressListener() { - @Override - public void progress(int p) { - startWork(AppStrings.translate("work.reading.swf"), p, worker); - } - }, Configuration.parallelSpeedUp.get()); - return swf; - } - }; - loadingDialog.setWroker(worker); - worker.execute(); - - try { - result.add(worker.get()); - } catch (CancellationException ex) { - logger.log(Level.WARNING, "Loading SWF {0} was cancelled.", streamEntry.getKey()); - } - } - } else { - InputStream fInputStream = inputStream; - - final String[] yesno = new String[]{AppStrings.translate("button.yes"), AppStrings.translate("button.no"), AppStrings.translate("button.yes.all"), AppStrings.translate("button.no.all")}; - - CancellableWorker worker = new CancellableWorker() { - private boolean yestoall = false; - - private boolean notoall = false; - - private SWF open(InputStream is, String file, String fileTitle) throws IOException, InterruptedException { - final CancellableWorker worker = this; - - SWF swf = new SWF(is, file, fileTitle, new ProgressListener() { - @Override - public void progress(int p) { - startWork(AppStrings.translate("work.reading.swf"), p, worker); - } - }, Configuration.parallelSpeedUp.get(), false, true, new UrlResolver() { - @Override - public SWF resolveUrl(final String url) { - int opt = -1; - if (!(yestoall || notoall)) { - opt = View.showOptionDialog(null, AppStrings.translate("message.imported.swf").replace("%url%", url), AppStrings.translate("message.warning"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, yesno, AppStrings.translate("button.yes")); - if (opt == 2) { - yestoall = true; - } - if (opt == 3) { - notoall = true; - } - } - - if (yestoall) { - opt = 0; // yes - } else if (notoall) { - opt = 1; // no - } - - if (opt == 1) //no - { - return null; - } - - if (url.startsWith("http://") || url.startsWith("https://")) { - try { - URL u = new URL(url); - return open(u.openStream(), null, url); //? - } catch (Exception ex) { - //ignore - } - } else { - File f = new File(new File(file).getParentFile(), url); - if (f.exists()) { - try { - return open(new FileInputStream(f), f.getAbsolutePath(), f.getName()); - } catch (Exception ex) { - //ignore - } - } - } - Reference ret = new Reference<>(null); - View.execInEventDispatch(new Runnable() { - @Override - public void run() { - - while (JOptionPane.YES_OPTION == View.showConfirmDialog(null, AppStrings.translate("message.imported.swf.manually").replace("%url%", url), AppStrings.translate("error"), JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE)) { - - JFileChooser fc = new JFileChooser(); - fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); - FileFilter allSupportedFilter = new FileFilter() { - private final String[] supportedExtensions = new String[]{".swf", ".gfx"}; - - @Override - public boolean accept(File f) { - String name = f.getName().toLowerCase(); - for (String ext : supportedExtensions) { - if (name.endsWith(ext)) { - return true; - } - } - return f.isDirectory(); - } - - @Override - public String getDescription() { - String exts = Helper.joinStrings(supportedExtensions, "*%s", "; "); - return AppStrings.translate("filter.supported") + " (" + exts + ")"; - } - }; - fc.setFileFilter(allSupportedFilter); - FileFilter swfFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".swf")) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.swf"); - } - }; - fc.addChoosableFileFilter(swfFilter); - - FileFilter gfxFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".gfx")) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.gfx"); - } - }; - fc.addChoosableFileFilter(gfxFilter); - fc.setAcceptAllFileFilterUsed(false); - JFrame f = new JFrame(); - View.setWindowIcon(f); - int returnVal = fc.showOpenDialog(f); - if (returnVal == JFileChooser.APPROVE_OPTION) { - Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); - File selFile = Helper.fixDialogFile(fc.getSelectedFile()); - try { - ret.setVal(open(new FileInputStream(selFile), selFile.getAbsolutePath(), selFile.getName())); - break; - } catch (Exception ex) { - //ignore; - } - } else { - break; - } - } - } - }); - return ret.getVal(); - } - }); - return swf; - } - - @Override - public SWF doInBackground() throws Exception { - return open(fInputStream, sourceInfo.getFile(), sourceInfo.getFileTitle()); - } - }; - if (loadingDialog != null) { - loadingDialog.setWroker(worker); - } - worker.execute(); - - try { - result.add(worker.get()); - } catch (CancellationException ex) { - logger.log(Level.WARNING, "Loading SWF {0} was cancelled.", sourceInfo.getFileTitleOrName()); - } - } - - if (fis != null) { - logger.log(Level.INFO, "File loaded in {0} seconds.", (sw.getElapsedMilliseconds() / 1000)); - fis.close(); - } else { - logger.log(Level.INFO, "Stream loaded in {0} seconds.", (sw.getElapsedMilliseconds() / 1000)); - } - - result.sourceInfo = sourceInfo; - for (SWF swf : result) { - logger.log(Level.INFO, ""); - logger.log(Level.INFO, "== File information =="); - logger.log(Level.INFO, "Size: {0}", Helper.formatFileSize(swf.fileSize)); - logger.log(Level.INFO, "Flash version: {0}", swf.version); - int width = (int) ((swf.displayRect.Xmax - swf.displayRect.Xmin) / SWF.unitDivisor); - int height = (int) ((swf.displayRect.Ymax - swf.displayRect.Ymin) / SWF.unitDivisor); - logger.log(Level.INFO, "Width: {0}", width); - logger.log(Level.INFO, "Height: {0}", height); - - swf.swfList = result; - swf.addEventListener(new EventListener() { - @Override - public void handleExportingEvent(String type, int index, int count, Object data) { - String text = AppStrings.translate("work.exporting"); - if (type != null && type.length() > 0) { - text += " " + type; - } - - startWork(text + " " + index + "/" + count + " " + data, null); - } - - @Override - public void handleExportedEvent(String type, int index, int count, Object data) { - String text = AppStrings.translate("work.exported"); - if (type != null && type.length() > 0) { - text += " " + type; - } - - startWork(text + " " + index + "/" + count + " " + data, null); - } - - @Override - public void handleEvent(String event, Object data) { - if (event.equals("exporting") || event.equals("exported")) { - throw new Error("Event is not supported by this handler."); - } - if (event.equals("getVariables")) { - startWork(AppStrings.translate("work.gettingvariables") + "..." + (String) data, null); - } - if (event.equals("deobfuscate")) { - startWork(AppStrings.translate("work.deobfuscating") + "..." + (String) data, null); - } - if (event.equals("rename")) { - startWork(AppStrings.translate("work.renaming") + "..." + (String) data, null); - } - } - }); - } - - return result; - } - - public static void saveFile(SWF swf, String outfile) throws IOException { - saveFile(swf, outfile, SaveFileMode.SAVE, null); - } - - public static void saveFile(SWF swf, String outfile, SaveFileMode mode, ExeExportMode exeExportMode) throws IOException { - if (mode == SaveFileMode.SAVEAS && !swf.swfList.isBundle()) { - swf.setFile(outfile); - swf.swfList.sourceInfo.setFile(outfile); - } - File outfileF = new File(outfile); - File tmpFile = new File(outfile + ".tmp"); - try (FileOutputStream fos = new FileOutputStream(tmpFile); - BufferedOutputStream bos = new BufferedOutputStream(fos)) { - if (mode == SaveFileMode.EXE) { - switch (exeExportMode) { - case WRAPPER: - InputStream exeStream = View.class.getClassLoader().getResourceAsStream("com/jpexs/helpers/resource/Swf2Exe.bin"); - Helper.copyStream(exeStream, bos); - int width = swf.displayRect.Xmax - swf.displayRect.Xmin; - int height = swf.displayRect.Ymax - swf.displayRect.Ymin; - bos.write(width & 0xff); - bos.write((width >> 8) & 0xff); - bos.write((width >> 16) & 0xff); - bos.write((width >> 24) & 0xff); - bos.write(height & 0xff); - bos.write((height >> 8) & 0xff); - bos.write((height >> 16) & 0xff); - bos.write((height >> 24) & 0xff); - bos.write(Configuration.saveAsExeScaleMode.get()); - break; - case PROJECTOR_WIN: - case PROJECTOR_MAC: - case PROJECTOR_LINUX: - File projectorFile = Configuration.getProjectorFile(exeExportMode); - if (projectorFile == null) { - String message = "Projector not found, please place it to " + Configuration.getProjectorPath(); - logger.log(Level.SEVERE, message); - throw new IOException(message); - } - Helper.copyStream(new FileInputStream(projectorFile), bos); - bos.flush(); - break; - } - } - - long pos = fos.getChannel().position(); - swf.saveTo(bos); - - if (mode == SaveFileMode.EXE) { - switch (exeExportMode) { - case PROJECTOR_WIN: - case PROJECTOR_MAC: - case PROJECTOR_LINUX: - bos.flush(); - int swfSize = (int) (fos.getChannel().position() - pos); - - // write magic number - bos.write(0x56); - bos.write(0x34); - bos.write(0x12); - bos.write(0xfa); - - bos.write(swfSize & 0xff); - bos.write((swfSize >> 8) & 0xff); - bos.write((swfSize >> 16) & 0xff); - bos.write((swfSize >> 24) & 0xff); - } - } - } - if (tmpFile.exists()) { - if (tmpFile.length() > 0) { - outfileF.delete(); - if (!tmpFile.renameTo(outfileF)) { - tmpFile.delete(); - throw new IOException("Cannot access " + outfile); - } - } else { - throw new IOException("Output is empty"); - } - } else { - throw new IOException("Output not found"); - } - } - - private static class OpenFileWorker extends SwingWorker { - - private final SWFSourceInfo[] sourceInfos; - - private final Runnable executeAfterOpen; - - private final int[] reloadIndices; - - public OpenFileWorker(SWFSourceInfo sourceInfo) { - this(sourceInfo, -1); - } - - public OpenFileWorker(SWFSourceInfo sourceInfo, int reloadIndex) { - this(sourceInfo, null, reloadIndex); - } - - public OpenFileWorker(SWFSourceInfo sourceInfo, Runnable executeAfterOpen) { - this(sourceInfo, executeAfterOpen, -1); - } - - public OpenFileWorker(SWFSourceInfo sourceInfo, Runnable executeAfterOpen, int reloadIndex) { - this.sourceInfos = new SWFSourceInfo[]{sourceInfo}; - this.executeAfterOpen = executeAfterOpen; - this.reloadIndices = new int[]{reloadIndex}; - } - - public OpenFileWorker(SWFSourceInfo[] sourceInfos) { - this(sourceInfos, null, null); - } - - public OpenFileWorker(SWFSourceInfo[] sourceInfos, Runnable executeAfterOpen) { - this(sourceInfos, executeAfterOpen, null); - } - - public OpenFileWorker(SWFSourceInfo[] sourceInfos, Runnable executeAfterOpen, int[] reloadIndices) { - this.sourceInfos = sourceInfos; - this.executeAfterOpen = executeAfterOpen; - int[] indices = new int[sourceInfos.length]; - for (int i = 0; i < indices.length; i++) { - indices[i] = -1; - } - this.reloadIndices = reloadIndices == null ? indices : reloadIndices; - } - - @Override - protected Object doInBackground() throws Exception { - boolean first = true; - SWF firstSWF = null; - for (int index = 0; index < sourceInfos.length; index++) { - SWFSourceInfo sourceInfo = sourceInfos[index]; - SWFList swfs = null; - try { - Main.startWork(AppStrings.translate("work.reading.swf") + "...", null); - try { - swfs = parseSWF(sourceInfo); - } catch (ExecutionException ex) { - Throwable cause = ex.getCause(); - if (cause instanceof SwfOpenException) { - throw (SwfOpenException) cause; - } - - throw ex; - } - } catch (OutOfMemoryError ex) { - logger.log(Level.SEVERE, null, ex); - View.showMessageDialog(null, "Cannot load SWF file. Out of memory."); - continue; - } catch (SwfOpenException ex) { - logger.log(Level.SEVERE, null, ex); - View.showMessageDialog(null, ex.getMessage()); - continue; - } catch (Exception ex) { - logger.log(Level.SEVERE, null, ex); - View.showMessageDialog(null, "Cannot load SWF file."); - continue; - } - - final SWFList swfs1 = swfs; - final boolean first1 = first; - first = false; - if (firstSWF == null && swfs1.size() > 0) { - firstSWF = swfs1.get(0); - } - - final int findex = index; - try { - View.execInEventDispatch(() -> { - Main.startWork(AppStrings.translate("work.creatingwindow") + "...", null); - ensureMainFrame(); - if (reloadIndices[findex] > -1) { - mainFrame.getPanel().loadSwfAtPos(swfs1, reloadIndices[findex]); - } else { - mainFrame.getPanel().load(swfs1, first1); - } - }); - } catch (Exception ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - loadingDialog.setVisible(false); - shouldCloseWhenClosingLoadingDialog = false; - - final SWF fswf = firstSWF; - View.execInEventDispatch(() -> { - if (mainFrame != null) { - mainFrame.setVisible(true); - } - - Main.stopWork(); - - if (mainFrame != null && Configuration.gotoMainClassOnStartup.get()) { - mainFrame.getPanel().gotoDocumentClass(fswf); - } - - if (mainFrame != null && fswf != null) { - SwfSpecificConfiguration swfConf = Configuration.getSwfSpecificConfiguration(fswf.getShortFileName()); - if (swfConf != null) { - String pathStr = swfConf.lastSelectedPath; - mainFrame.getPanel().tagTree.setSelectionPathString(pathStr); - } - } - - if (executeAfterOpen != null) { - executeAfterOpen.run(); - } - }); - - return true; - } - } - - public static boolean reloadSWFs() { - CancellableWorker.cancelBackgroundThreads(); - if (Main.sourceInfos.isEmpty()) { - Helper.freeMem(); - showModeFrame(); - return true; - } else { - SWFSourceInfo[] sourceInfosCopy = new SWFSourceInfo[sourceInfos.size()]; - sourceInfos.toArray(sourceInfosCopy); - sourceInfos.clear(); - openFile(sourceInfosCopy); - return true; - } - } - - public static void reloadApp() { - if (debugDialog != null) { - debugDialog.setVisible(false); - debugDialog.dispose(); - debugDialog = null; - } - if (loadingDialog != null) { - synchronized (Main.class) { - if (loadingDialog != null) { - loadingDialog.setVisible(false); - loadingDialog.dispose(); - loadingDialog = null; - } - } - } - if (proxyFrame != null) { - proxyFrame.setVisible(false); - proxyFrame.dispose(); - proxyFrame = null; - } - if (loadFromMemoryFrame != null) { - loadFromMemoryFrame.setVisible(false); - loadFromMemoryFrame.dispose(); - loadFromMemoryFrame = null; - } - if (loadFromCacheFrame != null) { - loadFromCacheFrame.setVisible(false); - loadFromCacheFrame.dispose(); - loadFromCacheFrame = null; - } - if (mainFrame != null) { - mainFrame.setVisible(false); - mainFrame.getPanel().closeAll(false); - mainFrame.dispose(); - mainFrame = null; - } - FontTag.reload(); - Cache.clearAll(); - initGui(); - reloadSWFs(); - } - - public static OpenFileResult openFile(String swfFile, String fileTitle) { - return openFile(swfFile, fileTitle, null); - } - - public static OpenFileResult openFile(String swfFile, String fileTitle, Runnable executeAfterOpen) { - try { - File file = new File(swfFile); - if (!file.exists()) { - View.showMessageDialog(null, AppStrings.translate("open.error.fileNotFound"), AppStrings.translate("open.error"), JOptionPane.ERROR_MESSAGE); - return OpenFileResult.NOT_FOUND; - } - swfFile = file.getCanonicalPath(); - Configuration.addRecentFile(swfFile); - SWFSourceInfo sourceInfo = new SWFSourceInfo(null, swfFile, fileTitle); - OpenFileResult openResult = openFile(sourceInfo); - return openResult; - } catch (IOException ex) { - View.showMessageDialog(null, AppStrings.translate("open.error.cannotOpen"), AppStrings.translate("open.error"), JOptionPane.ERROR_MESSAGE); - return OpenFileResult.ERROR; - } - } - - public static OpenFileResult openFile(SWFSourceInfo sourceInfo) { - return openFile(new SWFSourceInfo[]{sourceInfo}); - } - - public static OpenFileResult openFile(SWFSourceInfo sourceInfo, Runnable executeAfterOpen) { - return openFile(new SWFSourceInfo[]{sourceInfo}, executeAfterOpen); - } - - public static OpenFileResult openFile(SWFSourceInfo sourceInfo, Runnable executeAfterOpen, int reloadIndex) { - return openFile(new SWFSourceInfo[]{sourceInfo}, executeAfterOpen, new int[]{reloadIndex}); - } - - public static OpenFileResult openFile(SWFSourceInfo[] newSourceInfos) { - return openFile(newSourceInfos, null); - } - - public static OpenFileResult openFile(SWFSourceInfo[] newSourceInfos, Runnable executeAfterOpen) { - return openFile(newSourceInfos, executeAfterOpen, null); - } - - public static OpenFileResult openFile(SWFSourceInfo[] newSourceInfos, Runnable executeAfterOpen, int[] reloadIndices) { - if (mainFrame != null && !Configuration.openMultipleFiles.get()) { - sourceInfos.clear(); - mainFrame.getPanel().closeAll(false); - mainFrame.setVisible(false); - Helper.freeMem(); - reloadIndices = null; - } - - loadingDialog.setVisible(true); - - OpenFileWorker wrk = new OpenFileWorker(newSourceInfos, executeAfterOpen, reloadIndices); - wrk.execute(); - if (reloadIndices == null) { - sourceInfos.addAll(Arrays.asList(newSourceInfos)); - } else { - for (int i = 0; i < reloadIndices.length; i++) { - sourceInfos.set(reloadIndices[i], newSourceInfos[i]); - } - } - return OpenFileResult.OK; - } - - public static void closeFile(SWFList swf) { - sourceInfos.remove(swf.sourceInfo); - mainFrame.getPanel().close(swf); - } - - public static void reloadFile(SWFList swf) { - //mainFrame.getPanel().close(swf); - openFile(swf.sourceInfo, null, sourceInfos.indexOf(swf.sourceInfo)); - } - - public static boolean closeAll() { - boolean closeResult = mainFrame.getPanel().closeAll(true); - if (closeResult) { - sourceInfos.clear(); - } - - return closeResult; - } - - public static boolean saveFileDialog(SWF swf, final SaveFileMode mode) { - JFileChooser fc = new JFileChooser(); - fc.setCurrentDirectory(new File(Configuration.lastSaveDir.get())); - String ext = ".swf"; - switch (mode) { - case SAVE: - case SAVEAS: - if (swf.getFile() != null) { - ext = Path.getExtension(swf.getFile()); - } - break; - case EXE: - ext = ".exe"; - break; - } - - FileFilter swfFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".swf")) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.swf"); - } - }; - - FileFilter gfxFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".gfx")) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.gfx"); - } - }; - - ExeExportMode exeExportMode = null; - if (mode == SaveFileMode.EXE) { - exeExportMode = Configuration.exeExportMode.get(); - if (exeExportMode == null) { - exeExportMode = ExeExportMode.WRAPPER; - } - String filterDescription = null; - switch (exeExportMode) { - case WRAPPER: - case PROJECTOR_WIN: - ext = ".exe"; - filterDescription = "filter.exe"; - break; - case PROJECTOR_MAC: - ext = ".dmg"; - filterDescription = "filter.dmg"; - break; - case PROJECTOR_LINUX: - // linux projector is compressed with tar.gz - // todo: decompress - ext = ""; - filterDescription = "filter.linuxExe"; - break; - } - - String fext = ext; - String ffilterDescription = filterDescription; - FileFilter exeFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(fext)) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate(ffilterDescription); - } - }; - fc.setFileFilter(exeFilter); - } else if (swf.gfx) { - fc.addChoosableFileFilter(swfFilter); - fc.setFileFilter(gfxFilter); - } else { - fc.setFileFilter(swfFilter); - fc.addChoosableFileFilter(gfxFilter); - } - final String extension = ext; - fc.setAcceptAllFileFilterUsed(false); - JFrame f = new JFrame(); - View.setWindowIcon(f); - if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) { - File file = Helper.fixDialogFile(fc.getSelectedFile()); - FileFilter selFilter = fc.getFileFilter(); - try { - String fileName = file.getAbsolutePath(); - if (selFilter == swfFilter) { - if (!fileName.toLowerCase().endsWith(extension)) { - fileName += extension; - } - swf.gfx = false; - } - if (selFilter == gfxFilter) { - if (!fileName.toLowerCase().endsWith(".gfx")) { - fileName += ".gfx"; - } - swf.gfx = true; - } - Main.saveFile(swf, fileName, mode, exeExportMode); - Configuration.lastSaveDir.set(file.getParentFile().getAbsolutePath()); - return true; - } catch (IOException ex) { - View.showMessageDialog(null, AppStrings.translate("error.file.write")); - } - } - return false; - } - - public static boolean openFileDialog() { - JFileChooser fc = new JFileChooser(); - if (Configuration.openMultipleFiles.get()) { - fc.setMultiSelectionEnabled(true); - } - fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); - FileFilter allSupportedFilter = new FileFilter() { - private final String[] supportedExtensions = new String[]{".swf", ".gfx", ".swc", ".zip"}; - - @Override - public boolean accept(File f) { - String name = f.getName().toLowerCase(); - for (String ext : supportedExtensions) { - if (name.endsWith(ext)) { - return true; - } - } - return f.isDirectory(); - } - - @Override - public String getDescription() { - String exts = Helper.joinStrings(supportedExtensions, "*%s", "; "); - return AppStrings.translate("filter.supported") + " (" + exts + ")"; - } - }; - fc.setFileFilter(allSupportedFilter); - FileFilter swfFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".swf")) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.swf"); - } - }; - fc.addChoosableFileFilter(swfFilter); - - FileFilter swcFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".swc")) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.swc"); - } - }; - fc.addChoosableFileFilter(swcFilter); - - FileFilter gfxFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".gfx")) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.gfx"); - } - }; - fc.addChoosableFileFilter(gfxFilter); - - FileFilter zipFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".zip")) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.zip"); - } - }; - fc.addChoosableFileFilter(zipFilter); - - FileFilter binaryFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return true; - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.binary"); - } - }; - fc.addChoosableFileFilter(binaryFilter); - - fc.setAcceptAllFileFilterUsed(false); - JFrame f = new JFrame(); - View.setWindowIcon(f); - int returnVal = fc.showOpenDialog(f); - if (returnVal == JFileChooser.APPROVE_OPTION) { - Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); - File[] selFiles = fc.getSelectedFiles(); - for (File file : selFiles) { - File selfile = Helper.fixDialogFile(file); - Main.openFile(selfile.getAbsolutePath(), null); - } - return true; - } else { - return false; - } - } - - public static void displayErrorFrame() { - ErrorLogFrame.getInstance().setVisible(true); - } - - private static String md5(byte data[]) { - try { - java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5"); - byte[] array = md.digest(data); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < array.length; ++i) { - sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1, 3)); - } - return sb.toString(); - } catch (java.security.NoSuchAlgorithmException e) { - } - return null; - } - - private static void initGui() { - if (GraphicsEnvironment.isHeadless()) { - System.err.println("Error: Your system does not support Graphic User Interface"); - exit(); - } - - System.setProperty("sun.java2d.d3d", "false"); - System.setProperty("sun.java2d.noddraw", "true"); - - if (Configuration.hwAcceleratedGraphics.get()) { - System.setProperty("sun.java2d.opengl", Configuration._debugMode.get() ? "True" : "true"); - } else { - System.setProperty("sun.java2d.opengl", "false"); - } - - initUiLang(); - - if (Configuration.useRibbonInterface.get()) { - View.setLookAndFeel(); - } else { - try { - UIManager.put(SubstanceLookAndFeel.COLORIZATION_FACTOR, null); - UIManager.put("Tree.expandedIcon", null); - UIManager.put("Tree.collapsedIcon", null); - UIManager.put("ColorChooserUI", null); - UIManager.put("ColorChooser.swatchesRecentSwatchSize", null); - UIManager.put("ColorChooser.swatchesSwatchSize", null); - UIManager.put("RibbonApplicationMenuPopupPanelUI", null); - UIManager.put("RibbonApplicationMenuButtonUI", null); - UIManager.put("ProgressBarUI", null); - UIManager.put("TextField.background", null); - UIManager.put("FormattedTextField.background", null); - UIManager.put("CommandButtonUI", null); - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - View.execInEventDispatch(() -> { - ErrorLogFrame.createNewInstance(); - - autoCheckForUpdates(); - offerAssociation(); - loadingDialog = new LoadingDialog(); - - DebuggerTools.initDebugger().addMessageListener(new DebugListener() { - @Override - public void onMessage(String clientId, String msg) { - } - - @Override - public void onLoaderURL(String clientId, String url) { - } - - @Override - public void onLoaderBytes(String clientId, byte[] data) { - String hash = md5(data); - for (SWFList sl : Main.getMainFrame().getPanel().getSwfs()) { - for (int s = 0; s < sl.size(); s++) { - String t = sl.get(s).getFileTitle(); - if (t == null) { - t = ""; - } - if (t.endsWith(":" + hash)) { //this one is already opened - return; - } - } - } - SWF swf = Main.getMainFrame().getPanel().getCurrentSwf(); - - String title = swf == null ? "?" : swf.getFileTitle(); - title = title + ":" + hash; - String tfile; - try { - tfile = tempFile(title); - Helper.writeFile(tfile, data); - openFile(new SWFSourceInfo(null, tfile, title)); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Cannot create tempfile"); - } - } - - @Override - public void onFinish(String clientId) { - } - }); - - try { - flashDebugger = new Debugger(); - debugHandler = new DebuggerHandler(); - debugHandler.addBreakListener(new DebuggerHandler.BreakListener() { - @Override - public void doContinue() { - mainFrame.getPanel().clearDebuggerColors(); - } - - @Override - public void breakAt(String scriptName, int line, final int classIndex, final int traitIndex, final int methodIndex) { - View.execInEventDispatch(new Runnable() { - @Override - public void run() { - mainFrame.getPanel().gotoScriptLine(getMainFrame().getPanel().getCurrentSwf(), scriptName, line, classIndex, traitIndex, methodIndex); - } - }); - } - }); - debugHandler.addConnectionListener(new DebuggerHandler.ConnectionListener() { - @Override - public void connected() { - Main.mainFrame.getMenu().updateComponents(); - } - - @Override - public void disconnected() { - if (Main.mainFrame != null && Main.mainFrame.getPanel() != null) { - Main.mainFrame.getPanel().refreshBreakPoints(); - } - } - }); - flashDebugger.addConnectionListener(debugHandler); - } catch (IOException ex) { - logger.log(Level.SEVERE, "eeex", ex); - } - }); - } - - public static void startDebugger() { - flashDebugger.startDebugger(); - } - - public static void stopDebugger() { - flashDebugger.stopDebugger(); - } - - public static void showModeFrame() { - ensureMainFrame(); - mainFrame.setVisible(true); - } - - private static void offerAssociation() { - boolean offered = Configuration.offeredAssociation.get(); - if (!offered) { - if (Platform.isWindows()) { - if ((!ContextMenuTools.isAddedToContextMenu()) && View.showConfirmDialog(null, "Do you want to add FFDec to context menu of SWF files?\n(Can be changed later from main menu)", "Context menu", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { - ContextMenuTools.addToContextMenu(true, false); - } - } - - Configuration.offeredAssociation.set(true); - } - } - - public static void initUiLang() { - if (GraphicsEnvironment.isHeadless()) { //No GUI in OS - return; - } - try { - Class cl = Class.forName("org.pushingpixels.substance.api.SubstanceLookAndFeel"); - Field field = cl.getDeclaredField("LABEL_BUNDLE"); - field.setAccessible(true); - field.set(null, null); - } catch (Throwable ex) { - logger.log(Level.SEVERE, null, ex); - } - - UIManager.put("OptionPane.okButtonText", AppStrings.translate("button.ok")); - UIManager.put("OptionPane.yesButtonText", AppStrings.translate("button.yes")); - UIManager.put("OptionPane.noButtonText", AppStrings.translate("button.no")); - UIManager.put("OptionPane.cancelButtonText", AppStrings.translate("button.cancel")); - UIManager.put("OptionPane.messageDialogTitle", AppStrings.translate("dialog.message.title")); - UIManager.put("OptionPane.titleText", AppStrings.translate("dialog.select.title")); - - UIManager.put("FileChooser.acceptAllFileFilterText", AppStrings.translate("FileChooser.acceptAllFileFilterText")); - UIManager.put("FileChooser.lookInLabelText", AppStrings.translate("FileChooser.lookInLabelText")); - UIManager.put("FileChooser.cancelButtonText", AppStrings.translate("button.cancel")); - UIManager.put("FileChooser.cancelButtonToolTipText", AppStrings.translate("button.cancel")); - UIManager.put("FileChooser.openButtonText", AppStrings.translate("FileChooser.openButtonText")); - UIManager.put("FileChooser.openButtonToolTipText", AppStrings.translate("FileChooser.openButtonToolTipText")); - UIManager.put("FileChooser.filesOfTypeLabelText", AppStrings.translate("FileChooser.filesOfTypeLabelText")); - UIManager.put("FileChooser.fileNameLabelText", AppStrings.translate("FileChooser.fileNameLabelText")); - UIManager.put("FileChooser.listViewButtonToolTipText", AppStrings.translate("FileChooser.listViewButtonToolTipText")); - UIManager.put("FileChooser.listViewButtonAccessibleName", AppStrings.translate("FileChooser.listViewButtonAccessibleName")); - UIManager.put("FileChooser.detailsViewButtonToolTipText", AppStrings.translate("FileChooser.detailsViewButtonToolTipText")); - UIManager.put("FileChooser.detailsViewButtonAccessibleName", AppStrings.translate("FileChooser.detailsViewButtonAccessibleName")); - UIManager.put("FileChooser.upFolderToolTipText", AppStrings.translate("FileChooser.upFolderToolTipText")); - UIManager.put("FileChooser.upFolderAccessibleName", AppStrings.translate("FileChooser.upFolderAccessibleName")); - UIManager.put("FileChooser.homeFolderToolTipText", AppStrings.translate("FileChooser.homeFolderToolTipText")); - UIManager.put("FileChooser.homeFolderAccessibleName", AppStrings.translate("FileChooser.homeFolderAccessibleName")); - UIManager.put("FileChooser.fileNameHeaderText", AppStrings.translate("FileChooser.fileNameHeaderText")); - UIManager.put("FileChooser.fileSizeHeaderText", AppStrings.translate("FileChooser.fileSizeHeaderText")); - UIManager.put("FileChooser.fileTypeHeaderText", AppStrings.translate("FileChooser.fileTypeHeaderText")); - UIManager.put("FileChooser.fileDateHeaderText", AppStrings.translate("FileChooser.fileDateHeaderText")); - UIManager.put("FileChooser.fileAttrHeaderText", AppStrings.translate("FileChooser.fileAttrHeaderText")); - UIManager.put("FileChooser.openDialogTitleText", AppStrings.translate("FileChooser.openDialogTitleText")); - UIManager.put("FileChooser.directoryDescriptionText", AppStrings.translate("FileChooser.directoryDescriptionText")); - UIManager.put("FileChooser.directoryOpenButtonText", AppStrings.translate("FileChooser.directoryOpenButtonText")); - UIManager.put("FileChooser.directoryOpenButtonToolTipText", AppStrings.translate("FileChooser.directoryOpenButtonToolTipText")); - UIManager.put("FileChooser.fileDescriptionText", AppStrings.translate("FileChooser.fileDescriptionText")); - UIManager.put("FileChooser.fileNameLabelText", AppStrings.translate("FileChooser.fileNameLabelText")); - UIManager.put("FileChooser.helpButtonText", AppStrings.translate("FileChooser.helpButtonText")); - UIManager.put("FileChooser.helpButtonToolTipText", AppStrings.translate("FileChooser.helpButtonToolTipText")); - UIManager.put("FileChooser.newFolderAccessibleName", AppStrings.translate("FileChooser.newFolderAccessibleName")); - UIManager.put("FileChooser.newFolderErrorText", AppStrings.translate("FileChooser.newFolderErrorText")); - UIManager.put("FileChooser.newFolderToolTipText", AppStrings.translate("FileChooser.newFolderToolTipText")); - UIManager.put("FileChooser.other.newFolder", AppStrings.translate("FileChooser.other.newFolder")); - UIManager.put("FileChooser.other.newFolder.subsequent", AppStrings.translate("FileChooser.other.newFolder.subsequent")); - UIManager.put("FileChooser.win32.newFolder", AppStrings.translate("FileChooser.win32.newFolder")); - UIManager.put("FileChooser.win32.newFolder.subsequent", AppStrings.translate("FileChooser.win32.newFolder.subsequent")); - UIManager.put("FileChooser.saveButtonText", AppStrings.translate("FileChooser.saveButtonText")); - UIManager.put("FileChooser.saveButtonToolTipText", AppStrings.translate("FileChooser.saveButtonToolTipText")); - UIManager.put("FileChooser.saveDialogTitleText", AppStrings.translate("FileChooser.saveDialogTitleText")); - UIManager.put("FileChooser.saveInLabelText", AppStrings.translate("FileChooser.saveInLabelText")); - UIManager.put("FileChooser.updateButtonText", AppStrings.translate("FileChooser.updateButtonText")); - UIManager.put("FileChooser.updateButtonToolTipText", AppStrings.translate("FileChooser.updateButtonToolTipText")); - - UIManager.put("FileChooser.detailsViewActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.detailsViewActionLabel.textAndMnemonic")); - UIManager.put("FileChooser.detailsViewButtonToolTip.textAndMnemonic", AppStrings.translate("FileChooser.detailsViewButtonToolTip.textAndMnemonic")); - UIManager.put("FileChooser.fileAttrHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileAttrHeader.textAndMnemonic")); - UIManager.put("FileChooser.fileDateHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileDateHeader.textAndMnemonic")); - UIManager.put("FileChooser.fileNameHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileNameHeader.textAndMnemonic")); - UIManager.put("FileChooser.fileNameLabel.textAndMnemonic", AppStrings.translate("FileChooser.fileNameLabel.textAndMnemonic")); - UIManager.put("FileChooser.fileSizeHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileSizeHeader.textAndMnemonic")); - UIManager.put("FileChooser.fileTypeHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileTypeHeader.textAndMnemonic")); - UIManager.put("FileChooser.filesOfTypeLabel.textAndMnemonic", AppStrings.translate("FileChooser.filesOfTypeLabel.textAndMnemonic")); - UIManager.put("FileChooser.folderNameLabel.textAndMnemonic", AppStrings.translate("FileChooser.folderNameLabel.textAndMnemonic")); - UIManager.put("FileChooser.homeFolderToolTip.textAndMnemonic", AppStrings.translate("FileChooser.homeFolderToolTip.textAndMnemonic")); - UIManager.put("FileChooser.listViewActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.listViewActionLabel.textAndMnemonic")); - UIManager.put("FileChooser.listViewButtonToolTip.textAndMnemonic", AppStrings.translate("FileChooser.listViewButtonToolTip.textAndMnemonic")); - UIManager.put("FileChooser.lookInLabel.textAndMnemonic", AppStrings.translate("FileChooser.lookInLabel.textAndMnemonic")); - UIManager.put("FileChooser.newFolderActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.newFolderActionLabel.textAndMnemonic")); - UIManager.put("FileChooser.newFolderToolTip.textAndMnemonic", AppStrings.translate("FileChooser.newFolderToolTip.textAndMnemonic")); - UIManager.put("FileChooser.refreshActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.refreshActionLabel.textAndMnemonic")); - UIManager.put("FileChooser.saveInLabel.textAndMnemonic", AppStrings.translate("FileChooser.saveInLabel.textAndMnemonic")); - UIManager.put("FileChooser.upFolderToolTip.textAndMnemonic", AppStrings.translate("FileChooser.upFolderToolTip.textAndMnemonic")); - UIManager.put("FileChooser.viewMenuButtonAccessibleName", AppStrings.translate("FileChooser.viewMenuButtonAccessibleName")); - UIManager.put("FileChooser.viewMenuButtonToolTipText", AppStrings.translate("FileChooser.viewMenuButtonToolTipText")); - UIManager.put("FileChooser.viewMenuLabel.textAndMnemonic", AppStrings.translate("FileChooser.viewMenuLabel.textAndMnemonic")); - UIManager.put("FileChooser.newFolderActionLabelText", AppStrings.translate("FileChooser.newFolderActionLabelText")); - UIManager.put("FileChooser.listViewActionLabelText", AppStrings.translate("FileChooser.listViewActionLabelText")); - UIManager.put("FileChooser.detailsViewActionLabelText", AppStrings.translate("FileChooser.detailsViewActionLabelText")); - UIManager.put("FileChooser.refreshActionLabelText", AppStrings.translate("FileChooser.refreshActionLabelText")); - UIManager.put("FileChooser.sortMenuLabelText", AppStrings.translate("FileChooser.sortMenuLabelText")); - UIManager.put("FileChooser.viewMenuLabelText", AppStrings.translate("FileChooser.viewMenuLabelText")); - UIManager.put("FileChooser.fileSizeKiloBytes", AppStrings.translate("FileChooser.fileSizeKiloBytes")); - UIManager.put("FileChooser.fileSizeMegaBytes", AppStrings.translate("FileChooser.fileSizeMegaBytes")); - UIManager.put("FileChooser.fileSizeGigaBytes", AppStrings.translate("FileChooser.fileSizeGigaBytes")); - UIManager.put("FileChooser.folderNameLabelText", AppStrings.translate("FileChooser.folderNameLabelText")); - - UIManager.put("ColorChooser.okText", AppStrings.translate("ColorChooser.okText")); - UIManager.put("ColorChooser.cancelText", AppStrings.translate("ColorChooser.cancelText")); - UIManager.put("ColorChooser.resetText", AppStrings.translate("ColorChooser.resetText")); - UIManager.put("ColorChooser.previewText", AppStrings.translate("ColorChooser.previewText")); - UIManager.put("ColorChooser.swatchesNameText", AppStrings.translate("ColorChooser.swatchesNameText")); - UIManager.put("ColorChooser.swatchesRecentText", AppStrings.translate("ColorChooser.swatchesRecentText")); - UIManager.put("ColorChooser.sampleText", AppStrings.translate("ColorChooser.sampleText")); - - } - - public static void initLang() { - if (!Configuration.locale.hasValue()) { - if (Platform.isWindows()) { - //Load from Installer - String uninstKey = "{E618D276-6596-41F4-8A98-447D442A77DB}_is1"; - uninstKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + uninstKey; - try { - if (Advapi32Util.registryKeyExists(WinReg.HKEY_LOCAL_MACHINE, uninstKey)) { - if (Advapi32Util.registryValueExists(WinReg.HKEY_LOCAL_MACHINE, uninstKey, "NSIS: Language")) { - String installedLoc = Advapi32Util.registryGetStringValue(WinReg.HKEY_LOCAL_MACHINE, uninstKey, "NSIS: Language"); - int lcid = Integer.parseInt(installedLoc); - char[] buf = new char[9]; - int cnt = Kernel32.INSTANCE.GetLocaleInfo(lcid, Kernel32.LOCALE_SISO639LANGNAME, buf, 9); - String langCode = new String(buf, 0, cnt).trim().toLowerCase(); - - cnt = Kernel32.INSTANCE.GetLocaleInfo(lcid, Kernel32.LOCALE_SISO3166CTRYNAME, buf, 9); - String countryCode = new String(buf, 0, cnt).trim().toLowerCase(); - - List langs = Arrays.asList(SelectLanguageDialog.getAvailableLanguages()); - for (int i = 0; i < langs.size(); i++) { - langs.set(i, langs.get(i).toLowerCase()); - } - - String selectedLang = null; - - if (langs.contains(langCode + "-" + countryCode)) { - selectedLang = SelectLanguageDialog.getAvailableLanguages()[langs.indexOf(langCode + "-" + countryCode)]; - } else if (langs.contains(langCode)) { - selectedLang = SelectLanguageDialog.getAvailableLanguages()[langs.indexOf(langCode)]; - } - if (selectedLang != null) { - Configuration.locale.set(selectedLang); - } - } - } - } catch (Exception ex) { - //ignore - } - } - } - Locale.setDefault(Locale.forLanguageTag(Configuration.locale.get())); - AppStrings.updateLanguage(); - - Helper.decompilationErrorAdd = AppStrings.translate(Configuration.autoDeobfuscate.get() ? "deobfuscation.comment.failed" : "deobfuscation.comment.tryenable"); - } - - /** - * Clear old FFDec/JavactiveX temp files - */ - private static void clearTemp() { - String tempDirPath = System.getProperty("java.io.tmpdir"); - if (tempDirPath == null) { - return; - } - File tempDir = new File(tempDirPath); - if (!tempDir.exists()) { - return; - } - File[] delFiles = tempDir.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.matches("ffdec_cache.*\\.tmp") || name.matches("javactivex_.*\\.exe") || name.matches("temp[0-9]+\\.swf") || name.matches("ffdec_view_.*\\.swf"); - } - }); - - if (delFiles != null) { - for (File f : delFiles) { - try { - f.delete(); - } catch (Exception ex) { - //ignore - } - } - } - } - - /** - * @param args the command line arguments - * @throws IOException On error - */ - public static void main(String[] args) throws IOException { - setSessionLoaded(false); - clearTemp(); - - try { - SWFDecompilerPlugin.loadPlugins(); - } catch (Throwable ex) { - logger.log(Level.SEVERE, "Failed to load plugins", ex); - } - - AppStrings.setResourceClass(MainFrame.class); - initLogging(Configuration._debugMode.get()); - - initLang(); - - if (Configuration.cacheOnDisk.get()) { - Cache.setStorageType(Cache.STORAGE_FILES); - } else { - Cache.setStorageType(Cache.STORAGE_MEMORY); - } - - if (args.length == 0) { - initGui(); - View.execInEventDispatch(() -> { - if (Configuration.allowOnlyOneInstance.get() && FirstInstance.focus()) { //Try to focus first instance - Main.exit(); - } else { - showModeFrame(); - reloadLastSession(); - } - }); - } else { - setSessionLoaded(true); - String[] filesToOpen = CommandLineArgumentParser.parseArguments(args); - if (filesToOpen != null && filesToOpen.length > 0) { - View.execInEventDispatch(() -> { - initGui(); - shouldCloseWhenClosingLoadingDialog = true; - if (Configuration.allowOnlyOneInstance.get() && FirstInstance.openFiles(Arrays.asList(filesToOpen))) { //Try to open in first instance - Main.exit(); - } else { - for (String fileToOpen : filesToOpen) { - openFile(fileToOpen, null); - } - } - }); - } - } - } - - private static void reloadLastSession() { - boolean openingFiles = false; - if (Configuration.saveSessionOnExit.get()) { - String lastSession = Configuration.lastSessionFiles.get(); - if (lastSession != null && lastSession.length() > 0) { - String[] filesToOpen = lastSession.split(File.pathSeparator, -1); - List exfiles = new ArrayList<>(); - List extitles = new ArrayList<>(); - String lastSessionTitles = Configuration.lastSessionFileTitles.get(); - String[] fileTitles = new String[0]; - if (lastSessionTitles != null && !lastSessionTitles.isEmpty()) { - fileTitles = lastSessionTitles.split(File.pathSeparator, -1); - } - for (int i = 0; i < filesToOpen.length; i++) { - if (new File(filesToOpen[i]).exists()) { - exfiles.add(filesToOpen[i]); - if (fileTitles.length > i) { - extitles.add(fileTitles[i]); - } else { - extitles.add(null); - } - } - } - SWFSourceInfo[] sourceInfos = new SWFSourceInfo[exfiles.size()]; - for (int i = 0; i < exfiles.size(); i++) { - String extitle = extitles.get(i); - sourceInfos[i] = new SWFSourceInfo(null, exfiles.get(i), extitle == null || extitle.isEmpty() ? null : extitle); - } - if (sourceInfos.length > 0) { - openingFiles = true; - openFile(sourceInfos, () -> { - mainFrame.getPanel().tagTree.setSelectionPathString(Configuration.lastSessionSelection.get()); - setSessionLoaded(true); - }); - } - } - } - - if (!openingFiles) { - setSessionLoaded(true); - } - } - - public static String tempFile(String url) throws IOException { - File f = new File(Configuration.getFFDecHome() + "saved" + File.separator); - Path.createDirectorySafe(f); - return Configuration.getFFDecHome() + "saved" + File.separator + "asdec_" + Integer.toHexString(url.hashCode()) + ".tmp"; - } - - public static void removeTrayIcon() { - if (SystemTray.isSupported()) { - SystemTray tray = SystemTray.getSystemTray(); - if (trayIcon != null) { - tray.remove(trayIcon); - trayIcon = null; - } - } - } - - public static void switchProxy() { - proxyFrame.switchState(); - if (stopMenuItem != null) { - if (proxyFrame.isRunning()) { - stopMenuItem.setLabel(AppStrings.translate("proxy.stop")); - } else { - stopMenuItem.setLabel(AppStrings.translate("proxy.start")); - } - } - } - - public static void addTrayIcon() { - if (trayIcon != null) { - return; - } - if (SystemTray.isSupported()) { - SystemTray tray = SystemTray.getSystemTray(); - trayIcon = new TrayIcon(View.loadImage("proxy16"), ApplicationInfo.VENDOR + " " + ApplicationInfo.SHORT_APPLICATION_NAME + " " + AppStrings.translate("proxy")); - trayIcon.setImageAutoSize(true); - PopupMenu trayPopup = new PopupMenu(); - - ActionListener trayListener = new ActionListener() { - /** - * Invoked when an action occurs. - */ - @Override - public void actionPerformed(ActionEvent e) { - if (e.getActionCommand().equals("EXIT")) { - Main.exit(); - } - if (e.getActionCommand().equals("SHOW")) { - Main.showProxy(); - } - if (e.getActionCommand().equals("SWITCH")) { - Main.switchProxy(); - } - } - }; - - MenuItem showMenuItem = new MenuItem(AppStrings.translate("proxy.show")); - showMenuItem.setActionCommand("SHOW"); - showMenuItem.addActionListener(trayListener); - trayPopup.add(showMenuItem); - stopMenuItem = new MenuItem(AppStrings.translate("proxy.start")); - stopMenuItem.setActionCommand("SWITCH"); - stopMenuItem.addActionListener(trayListener); - trayPopup.add(stopMenuItem); - trayPopup.addSeparator(); - MenuItem exitMenuItem = new MenuItem(AppStrings.translate("exit")); - exitMenuItem.setActionCommand("EXIT"); - exitMenuItem.addActionListener(trayListener); - trayPopup.add(exitMenuItem); - - trayIcon.setPopupMenu(trayPopup); - trayIcon.addMouseListener(new MouseAdapter() { - /** - * {@inheritDoc} - */ - @Override - public void mouseClicked(MouseEvent e) { - if (e.getButton() == MouseEvent.BUTTON1) { - Main.showProxy(); - } - } - }); - try { - tray.add(trayIcon); - } catch (AWTException ex) { - } - } - } - - public static void exit() { - Configuration.saveConfig(); - if (mainFrame != null && mainFrame.getPanel() != null) { - mainFrame.getPanel().unloadFlashPlayer(); - mainFrame.dispose(); - } - if (fileTxt != null) { - try { - fileTxt.flush(); - fileTxt.close(); - } catch (Exception ex) { - //ignore - } - } - System.exit(0); - } - - public static void about() { - (new AboutDialog()).setVisible(true); - } - - public static void advancedSettings() { - advancedSettings(null); - } - - public static void advancedSettings(String category) { - (new AdvancedSettingsDialog(category)).setVisible(true); - } - - public static void autoCheckForUpdates() { - if (Configuration.checkForUpdatesAuto.get()) { - Calendar lastUpdatesCheckDate = Configuration.lastUpdatesCheckDate.get(); - if ((lastUpdatesCheckDate == null) || (lastUpdatesCheckDate.getTime().getTime() < Calendar.getInstance().getTime().getTime() - Configuration.checkForUpdatesDelay.get())) { - new SwingWorker() { - @Override - protected Object doInBackground() throws Exception { - checkForUpdates(); - return null; - } - }.execute(); - } - } - } - - public static boolean checkForUpdates() { - String currentVersion = ApplicationInfo.version; - if (currentVersion.equals("unknown")) { - // sometimes during development the version information is not available - return false; - } - - List accepted = new ArrayList<>(); - if (Configuration.checkForUpdatesStable.get()) { - accepted.add("stable"); - } - if (Configuration.checkForUpdatesNightly.get()) { - accepted.add("nightly"); - } - - if (accepted.isEmpty()) { - return false; - } - - String acceptVersions = String.join(",", accepted); - try { - String proxyAddress = Configuration.updateProxyAddress.get(); - URL url = new URL(ApplicationInfo.updateCheckUrl); - - URLConnection uc; - if (proxyAddress != null && !proxyAddress.isEmpty()) { - int port = 8080; - if (proxyAddress.contains(":")) { - String[] parts = proxyAddress.split(":"); - port = Integer.parseInt(parts[1]); - proxyAddress = parts[0]; - } - - uc = url.openConnection(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyAddress, port))); - } else { - uc = url.openConnection(); - } - uc.setRequestProperty("X-Accept-Versions", acceptVersions); - uc.setRequestProperty("X-Update-Major", "" + UPDATE_SYSTEM_MAJOR); - uc.setRequestProperty("X-Update-Minor", "" + UPDATE_SYSTEM_MINOR); - uc.setRequestProperty("User-Agent", ApplicationInfo.shortApplicationVerName); - String currentLoc = Configuration.locale.get("en"); - uc.setRequestProperty("Accept-Language", currentLoc + ("en".equals(currentLoc) ? "" : ", en;q=0.8")); - - uc.connect(); - - BufferedReader br = new BufferedReader(new InputStreamReader(uc.getInputStream())); - String s; - final java.util.List versions = new ArrayList<>(); - String header = ""; - Pattern headerPat = Pattern.compile("\\[([a-zA-Z0-9]+)\\]"); - int updateMajor; - int updateMinor; - Version ver = null; - while ((s = br.readLine()) != null) { - - Matcher m = headerPat.matcher(s); - if (m.matches()) { - header = m.group(1); - if (header.equals("version")) { - ver = new Version(); - versions.add(ver); - } - if (header.equals("noversion")) { - break; - } - } else if (s.contains("=")) { - String key = s.substring(0, s.indexOf('=')); - String val = s.substring(s.indexOf('=') + 1); - if ("updateSystem".equals(header)) { - if (key.equals("majorVersion")) { - updateMajor = Integer.parseInt(val); - if (updateMajor > UPDATE_SYSTEM_MAJOR) { - break; - } - } - if (key.equals("minorVersion")) { - updateMinor = Integer.parseInt(val); - } - } - if ("version".equals(header) && (ver != null)) { - if (key.equals("versionId")) { - ver.versionId = Integer.parseInt(val); - } - if (key.equals("versionName")) { - ver.versionName = val; - } - if (key.equals("nightly")) { - ver.nightly = val.equals("true"); - } - if (key.equals("revision")) { - ver.revision = val; - } - if (key.equals("build")) { - ver.build = Integer.parseInt(val); - } - if (key.equals("major")) { - ver.major = Integer.parseInt(val); - } - if (key.equals("minor")) { - ver.minor = Integer.parseInt(val); - } - if (key.equals("release")) { - ver.release = Integer.parseInt(val); - } - if (key.equals("longVersionName")) { - ver.longVersionName = val; - } - if (key.equals("releaseDate")) { - ver.releaseDate = val; - } - if (key.equals("appName")) { - ver.appName = val; - } - if (key.equals("appFullName")) { - ver.appFullName = val; - } - if (key.equals("updateLink")) { - ver.updateLink = val; - } - if (key.equals("change[]")) { - String changeType = val.substring(0, val.indexOf('|')); - String change = val.substring(val.indexOf('|') + 1); - if (!ver.changes.containsKey(changeType)) { - ver.changes.put(changeType, new ArrayList<>()); - } - List chlist = ver.changes.get(changeType); - chlist.add(change); - } - } - } - } - - if (!versions.isEmpty()) { - View.execInEventDispatch(() -> { - NewVersionDialog newVersionDialog = new NewVersionDialog(versions); - newVersionDialog.setVisible(true); - Configuration.lastUpdatesCheckDate.set(Calendar.getInstance()); - }); - - return true; - } - } catch (IOException | NumberFormatException ex) { - return false; - } - Configuration.lastUpdatesCheckDate.set(Calendar.getInstance()); - return false; - } - - private static FileHandler fileTxt; - - public static void clearLogFile() { - Logger logger = Logger.getLogger(""); - - FileHandler oldFileTxt = fileTxt; - fileTxt = null; - if (oldFileTxt != null) { - logger.removeHandler(fileTxt); - oldFileTxt.flush(); - oldFileTxt.close(); - } - - String fileName = null; - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss"); - - try { - fileName = Configuration.getFFDecHome() + "logs" + File.separator; - if (Configuration.useDetailedLogging.get()) { - fileName += "log-" + sdf.format(new Date()) + ".txt"; - } else { - fileName += "log.txt"; - } - File f = new File(fileName).getParentFile(); - if (!f.exists()) { - f.mkdir(); - } - fileTxt = new FileHandler(fileName); - } catch (IOException | SecurityException ex) { - //cannot get lock error - if (ex.getMessage().contains("lock for")) { - //remove all old log files and their .lck - for (int i = 0; i <= 100; i++) { - File flog = new File(fileName + (i == 0 ? "" : "." + i)); - File flog_lock = new File(fileName + (i == 0 ? "" : "." + i) + ".lck"); - flog.delete(); - flog_lock.delete(); - } - try { - fileTxt = new FileHandler(fileName); - } catch (IOException | SecurityException ex1) { - logger.log(Level.SEVERE, "Cannot initialize logging", ex); - } - } else { - logger.log(Level.SEVERE, "Cannot initialize logging", ex); - } - } - - Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { - @Override - public void uncaughtException(Thread t, Throwable e) { - logger.log(Level.SEVERE, "Uncaught exception in thread: " + t.getName(), e); - if (e instanceof OutOfMemoryError || !Helper.is64BitJre() && Helper.is64BitOs()) { - View.showMessageDialog(null, AppStrings.translate("message.warning.outOfMemory32BitJre"), AppStrings.translate("message.warning"), JOptionPane.WARNING_MESSAGE); - } - } - }); - - Formatter formatterTxt = new LogFormatter(); - if (fileTxt != null) { - fileTxt.setFormatter(formatterTxt); - logger.addHandler(fileTxt); - } - - if (!GraphicsEnvironment.isHeadless() && ErrorLogFrame.hasInstance()) { - ErrorLogFrame.getInstance().clearErrorState(); - } - - sdf = new SimpleDateFormat("yyyy-MM-dd"); - logger.log(Level.INFO, "Date: {0}", sdf.format(new Date())); - logger.log(Level.INFO, ApplicationInfo.applicationVerName); - logger.log(Level.INFO, "{0} {1} {2}", new Object[]{ - System.getProperty("os.name"), System.getProperty("os.version"), System.getProperty("os.arch")}); - logger.log(Level.INFO, "{0} {1} {2}", new Object[]{ - System.getProperty("java.version"), System.getProperty("java.vendor"), System.getProperty("os.arch")}); - } - - public static void initLogging(boolean debug) { - try { - Logger logger = Logger.getLogger(""); - logger.setLevel(Configuration.logLevel); - - Handler[] handlers = logger.getHandlers(); - for (int i = handlers.length - 1; i >= 0; i--) { - logger.removeHandler(handlers[i]); - } - - ConsoleHandler conHan = new ConsoleHandler(); - conHan.setLevel(debug ? Level.CONFIG : Level.WARNING); - SimpleFormatter formatterTxt = new SimpleFormatter(); - conHan.setFormatter(formatterTxt); - logger.addHandler(conHan); - clearLogFile(); - - } catch (Exception ex) { - throw new RuntimeException("Problems with creating the log files"); - } - } -} +/* + * Copyright (C) 2010-2016 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.debugger.flash.Debugger; +import com.jpexs.debugger.flash.DebuggerCommands; +import com.jpexs.debugger.flash.Variable; +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.SearchMode; +import com.jpexs.decompiler.flash.SwfOpenException; +import com.jpexs.decompiler.flash.UrlResolver; +import com.jpexs.decompiler.flash.Version; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.configuration.SwfSpecificConfiguration; +import com.jpexs.decompiler.flash.console.CommandLineArgumentParser; +import com.jpexs.decompiler.flash.console.ContextMenuTools; +import com.jpexs.decompiler.flash.exporters.modes.ExeExportMode; +import com.jpexs.decompiler.flash.gui.debugger.DebugListener; +import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools; +import com.jpexs.decompiler.flash.gui.pipes.FirstInstance; +import com.jpexs.decompiler.flash.gui.proxy.ProxyFrame; +import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.ImportTag; +import com.jpexs.decompiler.flash.treeitems.SWFList; +import com.jpexs.helpers.Cache; +import com.jpexs.helpers.CancellableWorker; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.Path; +import com.jpexs.helpers.ProgressListener; +import com.jpexs.helpers.Stopwatch; +import com.jpexs.helpers.streams.SeekableInputStream; +import com.sun.jna.Platform; +import com.sun.jna.platform.win32.Advapi32Util; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.WinReg; +import java.awt.AWTException; +import java.awt.Frame; +import java.awt.GraphicsEnvironment; +import java.awt.MenuItem; +import java.awt.PopupMenu; +import java.awt.SystemTray; +import java.awt.TrayIcon; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.logging.ConsoleHandler; +import java.util.logging.FileHandler; +import java.util.logging.Formatter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.SwingWorker; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.filechooser.FileFilter; +import org.pushingpixels.substance.api.SubstanceLookAndFeel; + +/** + * Main executable class + * + * @author JPEXS + */ +public class Main { + + protected static ProxyFrame proxyFrame; + + private static List sourceInfos = new ArrayList<>(); + + public static LoadingDialog loadingDialog; + + private static boolean working = false; + + private static TrayIcon trayIcon; + + private static MenuItem stopMenuItem; + + private static volatile MainFrame mainFrame; + + public static final int UPDATE_SYSTEM_MAJOR = 1; + + public static final int UPDATE_SYSTEM_MINOR = 3; + + private static LoadFromMemoryFrame loadFromMemoryFrame; + + private static LoadFromCacheFrame loadFromCacheFrame; + + private static final Logger logger = Logger.getLogger(Main.class.getName()); + + public static DebugLogDialog debugDialog; + + public static boolean shouldCloseWhenClosingLoadingDialog; + + private static Debugger flashDebugger; + + private static DebuggerHandler debugHandler = null; + + //private static int ip = 0; + //private static String ipClass = null; + private static Process runProcess; + + private static boolean runProcessDebug; + + private static boolean runProcessDebugPCode; + + private static boolean inited = false; + + private static File runTempFile; + + private static List runTempFiles = new ArrayList<>(); + + public static void freeRun() { + synchronized (Main.class) { + if (runTempFile != null) { + runTempFile.delete(); + runTempFile = null; + } + for (File f : runTempFiles) { + f.delete(); + } + runTempFiles.clear(); + + runProcess = null; + } + if (mainFrame != null && mainFrame.getPanel() != null) { + mainFrame.getPanel().clearDebuggerColors(); + } + if (runProcessDebug) { + Main.getDebugHandler().disconnect(); + } + } + + public static synchronized boolean isDebugPaused() { + return runProcess != null && runProcessDebug && getDebugHandler().isPaused(); + } + + public static synchronized boolean isDebugRunning() { + return runProcess != null && runProcessDebug; + } + + public static synchronized boolean isDebugPCode() { + return runProcessDebugPCode; + } + + public static synchronized boolean isDebugConnected() { + return getDebugHandler().isConnected(); + } + + public static synchronized boolean isRunning() { + return runProcess != null && !runProcessDebug; + } + + public static synchronized boolean addWatch(Variable v, long v_id, boolean watchRead, boolean watchWrite) { + DebuggerCommands.Watch w = getDebugHandler().addWatch(v, v_id, watchRead, watchWrite); + return w != null; + } + + public static void runPlayer(String title, final String exePath, String file, String flashVars) { + if (!new File(file).exists()) { + return; + } + if (flashVars != null && !flashVars.isEmpty()) { + file += "?" + flashVars; + } + final String ffile = file; + + CancellableWorker runWorker = new CancellableWorker() { + @Override + protected Object doInBackground() throws Exception { + Process proc; + try { + proc = Runtime.getRuntime().exec(new String[]{exePath, ffile}); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + return null; + } + boolean isDebug; + + synchronized (Main.class) { + runProcess = proc; + isDebug = runProcessDebug; + } + if (isDebug) { + mainFrame.getMenu().hilightPath("/debugging"); + } + mainFrame.getMenu().updateComponents(); + try { + if (proc != null) { + proc.waitFor(); + } + } catch (InterruptedException ex) { + if (proc != null) { + try { + proc.destroy(); + } catch (Exception ex2) { + //ignore + } + } + } + freeRun(); + stopDebugger(); + mainFrame.getMenu().updateComponents(); + return null; + } + + @Override + protected void done() { + Main.stopWork(); + } + + @Override + public void workerCancelled() { + Main.stopWork(); + synchronized (Main.class) { + if (runProcess != null) { + try { + runProcess.destroy(); + } catch (Exception ex) { + + } + } + } + freeRun(); + mainFrame.getMenu().updateComponents(); + } + }; + + mainFrame.getMenu().updateComponents(); + Main.startWork(title + "...", runWorker); + runWorker.execute(); + } + + public static void stopRun() { + + synchronized (Main.class) { + if (runProcess != null) { + runProcess.destroy(); + } + } + freeRun(); + stopDebugger(); + mainFrame.getMenu().updateComponents(); + } + + private static interface SwfPreparation { + + public SWF prepare(SWF swf); + } + + private static class SwfRunPrepare implements SwfPreparation { + + @Override + public SWF prepare(SWF swf) { + if (Configuration.autoOpenLoadedSWFs.get()) { + if (!DebuggerTools.hasDebugger(swf)) { + DebuggerTools.switchDebugger(swf); + } + DebuggerTools.injectDebugLoader(swf); + } + return swf; + } + } + + private static class SwfDebugPrepare extends SwfRunPrepare { + + private boolean doPCode; + + public SwfDebugPrepare(boolean doPCode) { + this.doPCode = doPCode; + } + + @Override + public SWF prepare(SWF instrSWF) { + instrSWF = super.prepare(instrSWF); + try { + File fTempFile = new File(instrSWF.getFile()); + instrSWF.enableDebugging(true, new File("."), true, doPCode); + FileOutputStream fos = new FileOutputStream(fTempFile); + instrSWF.saveTo(fos); + fos.close(); + if (!instrSWF.isAS3()) { + //Read again, because line file offsets changed with adding debug tags + //TODO: handle somehow without rereading? + instrSWF = null; + try (FileInputStream fis = new FileInputStream(fTempFile)) { + instrSWF = new SWF(fis, false, false); + } catch (InterruptedException ex) { + logger.log(Level.SEVERE, null, ex); + } + if (instrSWF != null) { + String swfFileName = fTempFile.getAbsolutePath(); + if (swfFileName.toLowerCase().endsWith(".swf")) { + swfFileName = swfFileName.substring(0, swfFileName.length() - 4) + ".swd"; + } else { + swfFileName = swfFileName + ".swd"; + } + File swdFile = new File(swfFileName); + if (doPCode) { + instrSWF.generatePCodeSwdFile(swdFile, getPackBreakPoints(true)); + } else { + instrSWF.generateSwdFile(swdFile, getPackBreakPoints(true)); + } + } + } + } catch (IOException ex) { + //ignore, return instrSWF + } + return instrSWF; + } + } + + private static void prepareSwf(SwfPreparation prep, File toPrepareFile, File origFile, List tempFiles) throws IOException { + SWF instrSWF = null; + try (FileInputStream fis = new FileInputStream(toPrepareFile)) { + instrSWF = new SWF(fis, toPrepareFile.getAbsolutePath(), origFile.getName(), false); + } catch (InterruptedException ex) { + logger.log(Level.SEVERE, null, ex); + } + if (instrSWF != null) { + for (Tag t : instrSWF.getLocalTags()) { + if (t instanceof ImportTag) { + ImportTag it = (ImportTag) t; + String url = it.getUrl(); + File importedFile = new File(origFile.getParentFile(), url); + if (importedFile.exists()) { + File newTempFile = File.createTempFile("ffdec_run_import_", ".swf"); + it.setUrl("./" + newTempFile.getName()); + byte[] impData = Helper.readFile(importedFile.getAbsolutePath()); + Helper.writeFile(newTempFile.getAbsolutePath(), impData); + tempFiles.add(newTempFile); + prepareSwf(prep, newTempFile, importedFile, tempFiles); + } + } + } + if (prep != null) { + instrSWF = prep.prepare(instrSWF); + } + try (FileOutputStream fos = new FileOutputStream(toPrepareFile)) { + instrSWF.saveTo(fos); + } + } + } + + public static void run(SWF swf) { + String flashVars = "";//key=val&key2=val2 + String playerLocation = Configuration.playerLocation.get(); + if (playerLocation.isEmpty() || (!new File(playerLocation).exists())) { + View.showMessageDialog(null, AppStrings.translate("message.playerpath.notset"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + advancedSettings("paths"); + return; + } + if (swf == null) { + return; + } + File tempFile; + List tempFiles = new ArrayList<>(); + try { + tempFile = File.createTempFile("ffdec_run_", ".swf"); + + try (FileOutputStream fos = new FileOutputStream(tempFile)) { + swf.saveTo(fos); + } + + prepareSwf(new SwfRunPrepare(), tempFile, new File(swf.getFile()), tempFiles); + + } catch (IOException ex) { + return; + + } + if (tempFile != null) { + synchronized (Main.class) { + runTempFile = tempFile; + runTempFiles = tempFiles; + runProcessDebug = false; + } + runPlayer(AppStrings.translate("work.running"), playerLocation, tempFile.getAbsolutePath(), flashVars); + } + } + + public static void runDebug(SWF swf, final boolean doPCode) { + String flashVars = "";//key=val&key2=val2 + String playerLocation = Configuration.playerDebugLocation.get(); + if (playerLocation.isEmpty() || (!new File(playerLocation).exists())) { + View.showMessageDialog(null, AppStrings.translate("message.playerpath.debug.notset"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + Main.advancedSettings("paths"); + return; + } + if (swf == null) { + return; + } + File tempFile = null; + + try { + tempFile = File.createTempFile("ffdec_debug_", ".swf"); + } catch (Exception ex) { + + } + + if (tempFile != null) { + final File fTempFile = tempFile; + final List tempFiles = new ArrayList<>(); + CancellableWorker instrumentWorker = new CancellableWorker() { + @Override + protected Object doInBackground() throws Exception { + + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(fTempFile))) { + swf.saveTo(fos); + } + prepareSwf(new SwfDebugPrepare(doPCode), fTempFile, new File(swf.getFile()), tempFiles); + return null; + } + + @Override + public void workerCancelled() { + Main.stopWork(); + } + + @Override + protected void done() { + synchronized (Main.class) { + runTempFile = fTempFile; + runProcessDebug = true; + runProcessDebugPCode = doPCode; + runTempFiles = tempFiles; + } + Main.stopWork(); + Main.startDebugger(); + runPlayer(AppStrings.translate("work.debugging.wait"), playerLocation, fTempFile.getAbsolutePath(), flashVars); + } + }; + + Main.startWork(AppStrings.translate("work.debugging.instrumenting"), instrumentWorker); + instrumentWorker.execute(); + } + } + + /* public static void debuggerNotSuspended() { + + }*/ + public static boolean isDebugging() { + return isDebugRunning(); + } + + public synchronized static int getIp(Object pack) { + return getDebugHandler().getBreakIp(); + } + + public synchronized static String getIpClass() { + return getDebugHandler().getBreakScriptName(); + } + + public static synchronized boolean isBreakPointValid(String scriptName, int line) { + return !getDebugHandler().isBreakpointInvalid(scriptName, line); + } + + public synchronized static void addBreakPoint(String scriptName, int line) { + getDebugHandler().addBreakPoint(scriptName, line); + } + + public synchronized static void removeBreakPoint(String scriptName, int line) { + getDebugHandler().removeBreakPoint(scriptName, line); + } + + public synchronized static boolean toggleBreakPoint(String scriptName, int line) { + if (getDebugHandler().isBreakpointToAdd(scriptName, line) || getDebugHandler().isBreakpointConfirmed(scriptName, line) || getDebugHandler().isBreakpointInvalid(scriptName, line)) { + getDebugHandler().removeBreakPoint(scriptName, line); + return false; + } else { + getDebugHandler().addBreakPoint(scriptName, line); + return true; + } + } + + public synchronized static Map> getPackBreakPoints(boolean validOnly) { + return getDebugHandler().getAllBreakPoints(validOnly); + } + + public synchronized static Set getScriptBreakPoints(String pack, boolean onlyValid) { + return getDebugHandler().getBreakPoints(pack, onlyValid); + } + + public static DebuggerHandler getDebugHandler() { + return debugHandler; + } + + public static void ensureMainFrame() { + if (mainFrame == null) { + synchronized (Main.class) { + if (mainFrame == null) { + MainFrame frame; + if (Configuration.useRibbonInterface.get()) { + frame = new MainFrameRibbon(); + } else { + frame = new MainFrameClassic(); + } + frame.getPanel().setErrorState(ErrorLogFrame.getInstance().getErrorState()); + mainFrame = frame; + } + } + } + } + + public static MainFrame getMainFrame() { + return mainFrame; + } + + public static void loadFromCache() { + if (loadFromCacheFrame == null) { + loadFromCacheFrame = new LoadFromCacheFrame(); + } + loadFromCacheFrame.setVisible(true); + } + + public static void loadFromMemory() { + if (loadFromMemoryFrame == null) { + loadFromMemoryFrame = new LoadFromMemoryFrame(mainFrame); + } + loadFromMemoryFrame.setVisible(true); + } + + public static void setVariable(long parentId, String varName, int valueType, Object value) { + getDebugHandler().setVariable(parentId, varName, valueType, value); + } + + public static void setSubLimiter(boolean value) { + if (value) { + AVM2Code.toSourceLimit = Configuration.sublimiter.get(); + } else { + AVM2Code.toSourceLimit = -1; + } + } + + public synchronized static boolean isInited() { + return inited; + } + + public synchronized static void setSessionLoaded(boolean v) { + inited = v; + } + + public static boolean isWorking() { + return working; + } + + public static void startProxy(int port) { + if (proxyFrame == null) { + proxyFrame = new ProxyFrame(mainFrame); + } + + proxyFrame.setPort(port); + addTrayIcon(); + switchProxy(); + } + + public static void showProxy() { + if (proxyFrame == null) { + proxyFrame = new ProxyFrame(mainFrame); + } + proxyFrame.setVisible(true); + proxyFrame.setState(Frame.NORMAL); + } + + public static void startWork(String name, CancellableWorker worker) { + startWork(name, -1, worker); + } + + public static void startWork(final String name, final int percent, final CancellableWorker worker) { + working = true; + View.execInEventDispatchLater(() -> { + if (mainFrame != null) { + mainFrame.getPanel().setWorkStatus(name, worker); + if (percent == -1) { + mainFrame.getPanel().hidePercent(); + } else { + mainFrame.getPanel().setPercent(percent); + } + } + if (loadingDialog != null) { + loadingDialog.setDetail(name); + loadingDialog.setPercent(percent); + } + if (CommandLineArgumentParser.isCommandLineMode()) { + System.out.println(name); + } + }); + } + + public static void stopWork() { + working = false; + View.execInEventDispatchLater(() -> { + if (mainFrame != null) { + mainFrame.getPanel().setWorkStatus("", null); + } + if (loadingDialog != null) { + loadingDialog.setDetail(""); + } + }); + } + + public static SWFList parseSWF(SWFSourceInfo sourceInfo) throws Exception { + SWFList result = new SWFList(); + + InputStream inputStream = sourceInfo.getInputStream(); + SWFBundle bundle = null; + FileInputStream fis = null; + if (inputStream == null) { + inputStream = new BufferedInputStream(fis = new FileInputStream(sourceInfo.getFile())); + bundle = sourceInfo.getBundle(false, SearchMode.ALL); + logger.log(Level.INFO, "Load file: {0}", sourceInfo.getFile()); + } else if (inputStream instanceof SeekableInputStream + || inputStream instanceof BufferedInputStream) { + try { + inputStream.reset(); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + logger.log(Level.INFO, "Load stream: {0}", sourceInfo.getFileTitle()); + } + + Stopwatch sw = Stopwatch.startNew(); + if (bundle != null) { + result.bundle = bundle; + result.name = new File(sourceInfo.getFileTitleOrName()).getName(); + for (Entry streamEntry : bundle.getAll().entrySet()) { + InputStream stream = streamEntry.getValue(); + stream.reset(); + CancellableWorker worker = new CancellableWorker() { + @Override + public SWF doInBackground() throws Exception { + final CancellableWorker worker = this; + SWF swf = new SWF(stream, null, streamEntry.getKey(), new ProgressListener() { + @Override + public void progress(int p) { + startWork(AppStrings.translate("work.reading.swf"), p, worker); + } + }, Configuration.parallelSpeedUp.get()); + return swf; + } + }; + loadingDialog.setWroker(worker); + worker.execute(); + + try { + result.add(worker.get()); + } catch (CancellationException ex) { + logger.log(Level.WARNING, "Loading SWF {0} was cancelled.", streamEntry.getKey()); + } + } + } else { + InputStream fInputStream = inputStream; + + final String[] yesno = new String[]{AppStrings.translate("button.yes"), AppStrings.translate("button.no"), AppStrings.translate("button.yes.all"), AppStrings.translate("button.no.all")}; + + CancellableWorker worker = new CancellableWorker() { + private boolean yestoall = false; + + private boolean notoall = false; + + private SWF open(InputStream is, String file, String fileTitle) throws IOException, InterruptedException { + final CancellableWorker worker = this; + + SWF swf = new SWF(is, file, fileTitle, new ProgressListener() { + @Override + public void progress(int p) { + startWork(AppStrings.translate("work.reading.swf"), p, worker); + } + }, Configuration.parallelSpeedUp.get(), false, true, new UrlResolver() { + @Override + public SWF resolveUrl(final String url) { + int opt = -1; + if (!(yestoall || notoall)) { + opt = View.showOptionDialog(null, AppStrings.translate("message.imported.swf").replace("%url%", url), AppStrings.translate("message.warning"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, yesno, AppStrings.translate("button.yes")); + if (opt == 2) { + yestoall = true; + } + if (opt == 3) { + notoall = true; + } + } + + if (yestoall) { + opt = 0; // yes + } else if (notoall) { + opt = 1; // no + } + + if (opt == 1) //no + { + return null; + } + + if (url.startsWith("http://") || url.startsWith("https://")) { + try { + URL u = new URL(url); + return open(u.openStream(), null, url); //? + } catch (Exception ex) { + //ignore + } + } else { + File f = new File(new File(file).getParentFile(), url); + if (f.exists()) { + try { + return open(new FileInputStream(f), f.getAbsolutePath(), f.getName()); + } catch (Exception ex) { + //ignore + } + } + } + Reference ret = new Reference<>(null); + View.execInEventDispatch(new Runnable() { + @Override + public void run() { + + while (JOptionPane.YES_OPTION == View.showConfirmDialog(null, AppStrings.translate("message.imported.swf.manually").replace("%url%", url), AppStrings.translate("error"), JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE)) { + + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); + FileFilter allSupportedFilter = new FileFilter() { + private final String[] supportedExtensions = new String[]{".swf", ".gfx"}; + + @Override + public boolean accept(File f) { + String name = f.getName().toLowerCase(); + for (String ext : supportedExtensions) { + if (name.endsWith(ext)) { + return true; + } + } + return f.isDirectory(); + } + + @Override + public String getDescription() { + String exts = Helper.joinStrings(supportedExtensions, "*%s", "; "); + return AppStrings.translate("filter.supported") + " (" + exts + ")"; + } + }; + fc.setFileFilter(allSupportedFilter); + FileFilter swfFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".swf")) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.swf"); + } + }; + fc.addChoosableFileFilter(swfFilter); + + FileFilter gfxFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".gfx")) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.gfx"); + } + }; + fc.addChoosableFileFilter(gfxFilter); + fc.setAcceptAllFileFilterUsed(false); + JFrame f = new JFrame(); + View.setWindowIcon(f); + int returnVal = fc.showOpenDialog(f); + if (returnVal == JFileChooser.APPROVE_OPTION) { + Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); + File selFile = Helper.fixDialogFile(fc.getSelectedFile()); + try { + ret.setVal(open(new FileInputStream(selFile), selFile.getAbsolutePath(), selFile.getName())); + break; + } catch (Exception ex) { + //ignore; + } + } else { + break; + } + } + } + }); + return ret.getVal(); + } + }); + return swf; + } + + @Override + public SWF doInBackground() throws Exception { + return open(fInputStream, sourceInfo.getFile(), sourceInfo.getFileTitle()); + } + }; + if (loadingDialog != null) { + loadingDialog.setWroker(worker); + } + worker.execute(); + + try { + result.add(worker.get()); + } catch (CancellationException ex) { + logger.log(Level.WARNING, "Loading SWF {0} was cancelled.", sourceInfo.getFileTitleOrName()); + } + } + + if (fis != null) { + logger.log(Level.INFO, "File loaded in {0} seconds.", (sw.getElapsedMilliseconds() / 1000)); + fis.close(); + } else { + logger.log(Level.INFO, "Stream loaded in {0} seconds.", (sw.getElapsedMilliseconds() / 1000)); + } + + result.sourceInfo = sourceInfo; + for (SWF swf : result) { + logger.log(Level.INFO, ""); + logger.log(Level.INFO, "== File information =="); + logger.log(Level.INFO, "Size: {0}", Helper.formatFileSize(swf.fileSize)); + logger.log(Level.INFO, "Flash version: {0}", swf.version); + int width = (int) ((swf.displayRect.Xmax - swf.displayRect.Xmin) / SWF.unitDivisor); + int height = (int) ((swf.displayRect.Ymax - swf.displayRect.Ymin) / SWF.unitDivisor); + logger.log(Level.INFO, "Width: {0}", width); + logger.log(Level.INFO, "Height: {0}", height); + + swf.swfList = result; + swf.addEventListener(new EventListener() { + @Override + public void handleExportingEvent(String type, int index, int count, Object data) { + String text = AppStrings.translate("work.exporting"); + if (type != null && type.length() > 0) { + text += " " + type; + } + + startWork(text + " " + index + "/" + count + " " + data, null); + } + + @Override + public void handleExportedEvent(String type, int index, int count, Object data) { + String text = AppStrings.translate("work.exported"); + if (type != null && type.length() > 0) { + text += " " + type; + } + + startWork(text + " " + index + "/" + count + " " + data, null); + } + + @Override + public void handleEvent(String event, Object data) { + if (event.equals("exporting") || event.equals("exported")) { + throw new Error("Event is not supported by this handler."); + } + if (event.equals("getVariables")) { + startWork(AppStrings.translate("work.gettingvariables") + "..." + (String) data, null); + } + if (event.equals("deobfuscate")) { + startWork(AppStrings.translate("work.deobfuscating") + "..." + (String) data, null); + } + if (event.equals("rename")) { + startWork(AppStrings.translate("work.renaming") + "..." + (String) data, null); + } + } + }); + } + + return result; + } + + public static void saveFile(SWF swf, String outfile) throws IOException { + saveFile(swf, outfile, SaveFileMode.SAVE, null); + } + + public static void saveFile(SWF swf, String outfile, SaveFileMode mode, ExeExportMode exeExportMode) throws IOException { + if (mode == SaveFileMode.SAVEAS && !swf.swfList.isBundle()) { + swf.setFile(outfile); + swf.swfList.sourceInfo.setFile(outfile); + } + File outfileF = new File(outfile); + File tmpFile = new File(outfile + ".tmp"); + try (FileOutputStream fos = new FileOutputStream(tmpFile); + BufferedOutputStream bos = new BufferedOutputStream(fos)) { + if (mode == SaveFileMode.EXE) { + switch (exeExportMode) { + case WRAPPER: + InputStream exeStream = View.class.getClassLoader().getResourceAsStream("com/jpexs/helpers/resource/Swf2Exe.bin"); + Helper.copyStream(exeStream, bos); + int width = swf.displayRect.Xmax - swf.displayRect.Xmin; + int height = swf.displayRect.Ymax - swf.displayRect.Ymin; + bos.write(width & 0xff); + bos.write((width >> 8) & 0xff); + bos.write((width >> 16) & 0xff); + bos.write((width >> 24) & 0xff); + bos.write(height & 0xff); + bos.write((height >> 8) & 0xff); + bos.write((height >> 16) & 0xff); + bos.write((height >> 24) & 0xff); + bos.write(Configuration.saveAsExeScaleMode.get()); + break; + case PROJECTOR_WIN: + case PROJECTOR_MAC: + case PROJECTOR_LINUX: + File projectorFile = Configuration.getProjectorFile(exeExportMode); + if (projectorFile == null) { + String message = "Projector not found, please place it to " + Configuration.getProjectorPath(); + logger.log(Level.SEVERE, message); + throw new IOException(message); + } + Helper.copyStream(new FileInputStream(projectorFile), bos); + bos.flush(); + break; + } + } + + long pos = fos.getChannel().position(); + swf.saveTo(bos); + + if (mode == SaveFileMode.EXE) { + switch (exeExportMode) { + case PROJECTOR_WIN: + case PROJECTOR_MAC: + case PROJECTOR_LINUX: + bos.flush(); + int swfSize = (int) (fos.getChannel().position() - pos); + + // write magic number + bos.write(0x56); + bos.write(0x34); + bos.write(0x12); + bos.write(0xfa); + + bos.write(swfSize & 0xff); + bos.write((swfSize >> 8) & 0xff); + bos.write((swfSize >> 16) & 0xff); + bos.write((swfSize >> 24) & 0xff); + } + } + } + if (tmpFile.exists()) { + if (tmpFile.length() > 0) { + outfileF.delete(); + if (!tmpFile.renameTo(outfileF)) { + tmpFile.delete(); + throw new IOException("Cannot access " + outfile); + } + } else { + throw new IOException("Output is empty"); + } + } else { + throw new IOException("Output not found"); + } + } + + private static class OpenFileWorker extends SwingWorker { + + private final SWFSourceInfo[] sourceInfos; + + private final Runnable executeAfterOpen; + + private final int[] reloadIndices; + + public OpenFileWorker(SWFSourceInfo sourceInfo) { + this(sourceInfo, -1); + } + + public OpenFileWorker(SWFSourceInfo sourceInfo, int reloadIndex) { + this(sourceInfo, null, reloadIndex); + } + + public OpenFileWorker(SWFSourceInfo sourceInfo, Runnable executeAfterOpen) { + this(sourceInfo, executeAfterOpen, -1); + } + + public OpenFileWorker(SWFSourceInfo sourceInfo, Runnable executeAfterOpen, int reloadIndex) { + this.sourceInfos = new SWFSourceInfo[]{sourceInfo}; + this.executeAfterOpen = executeAfterOpen; + this.reloadIndices = new int[]{reloadIndex}; + } + + public OpenFileWorker(SWFSourceInfo[] sourceInfos) { + this(sourceInfos, null, null); + } + + public OpenFileWorker(SWFSourceInfo[] sourceInfos, Runnable executeAfterOpen) { + this(sourceInfos, executeAfterOpen, null); + } + + public OpenFileWorker(SWFSourceInfo[] sourceInfos, Runnable executeAfterOpen, int[] reloadIndices) { + this.sourceInfos = sourceInfos; + this.executeAfterOpen = executeAfterOpen; + int[] indices = new int[sourceInfos.length]; + for (int i = 0; i < indices.length; i++) { + indices[i] = -1; + } + this.reloadIndices = reloadIndices == null ? indices : reloadIndices; + } + + @Override + protected Object doInBackground() throws Exception { + boolean first = true; + SWF firstSWF = null; + for (int index = 0; index < sourceInfos.length; index++) { + SWFSourceInfo sourceInfo = sourceInfos[index]; + SWFList swfs = null; + try { + Main.startWork(AppStrings.translate("work.reading.swf") + "...", null); + try { + swfs = parseSWF(sourceInfo); + } catch (ExecutionException ex) { + Throwable cause = ex.getCause(); + if (cause instanceof SwfOpenException) { + throw (SwfOpenException) cause; + } + + throw ex; + } + } catch (OutOfMemoryError ex) { + logger.log(Level.SEVERE, null, ex); + View.showMessageDialog(null, "Cannot load SWF file. Out of memory."); + continue; + } catch (SwfOpenException ex) { + logger.log(Level.SEVERE, null, ex); + View.showMessageDialog(null, ex.getMessage()); + continue; + } catch (Exception ex) { + logger.log(Level.SEVERE, null, ex); + View.showMessageDialog(null, "Cannot load SWF file."); + continue; + } + + final SWFList swfs1 = swfs; + final boolean first1 = first; + first = false; + if (firstSWF == null && swfs1.size() > 0) { + firstSWF = swfs1.get(0); + } + + final int findex = index; + try { + View.execInEventDispatch(() -> { + Main.startWork(AppStrings.translate("work.creatingwindow") + "...", null); + ensureMainFrame(); + if (reloadIndices[findex] > -1) { + mainFrame.getPanel().loadSwfAtPos(swfs1, reloadIndices[findex]); + } else { + mainFrame.getPanel().load(swfs1, first1); + } + }); + } catch (Exception ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + loadingDialog.setVisible(false); + shouldCloseWhenClosingLoadingDialog = false; + + final SWF fswf = firstSWF; + View.execInEventDispatch(() -> { + if (mainFrame != null) { + mainFrame.setVisible(true); + } + + Main.stopWork(); + + if (mainFrame != null && Configuration.gotoMainClassOnStartup.get()) { + mainFrame.getPanel().gotoDocumentClass(fswf); + } + + if (mainFrame != null && fswf != null) { + SwfSpecificConfiguration swfConf = Configuration.getSwfSpecificConfiguration(fswf.getShortFileName()); + if (swfConf != null) { + String pathStr = swfConf.lastSelectedPath; + mainFrame.getPanel().tagTree.setSelectionPathString(pathStr); + } + } + + if (executeAfterOpen != null) { + executeAfterOpen.run(); + } + }); + + return true; + } + } + + public static boolean reloadSWFs() { + CancellableWorker.cancelBackgroundThreads(); + if (Main.sourceInfos.isEmpty()) { + Helper.freeMem(); + showModeFrame(); + return true; + } else { + SWFSourceInfo[] sourceInfosCopy = new SWFSourceInfo[sourceInfos.size()]; + sourceInfos.toArray(sourceInfosCopy); + sourceInfos.clear(); + openFile(sourceInfosCopy); + return true; + } + } + + public static void reloadApp() { + if (debugDialog != null) { + debugDialog.setVisible(false); + debugDialog.dispose(); + debugDialog = null; + } + if (loadingDialog != null) { + synchronized (Main.class) { + if (loadingDialog != null) { + loadingDialog.setVisible(false); + loadingDialog.dispose(); + loadingDialog = null; + } + } + } + if (proxyFrame != null) { + proxyFrame.setVisible(false); + proxyFrame.dispose(); + proxyFrame = null; + } + if (loadFromMemoryFrame != null) { + loadFromMemoryFrame.setVisible(false); + loadFromMemoryFrame.dispose(); + loadFromMemoryFrame = null; + } + if (loadFromCacheFrame != null) { + loadFromCacheFrame.setVisible(false); + loadFromCacheFrame.dispose(); + loadFromCacheFrame = null; + } + if (mainFrame != null) { + mainFrame.setVisible(false); + mainFrame.getPanel().closeAll(false); + mainFrame.dispose(); + mainFrame = null; + } + FontTag.reload(); + Cache.clearAll(); + initGui(); + reloadSWFs(); + } + + public static OpenFileResult openFile(String swfFile, String fileTitle) { + return openFile(swfFile, fileTitle, null); + } + + public static OpenFileResult openFile(String swfFile, String fileTitle, Runnable executeAfterOpen) { + try { + File file = new File(swfFile); + if (!file.exists()) { + View.showMessageDialog(null, AppStrings.translate("open.error.fileNotFound"), AppStrings.translate("open.error"), JOptionPane.ERROR_MESSAGE); + return OpenFileResult.NOT_FOUND; + } + swfFile = file.getCanonicalPath(); + Configuration.addRecentFile(swfFile); + SWFSourceInfo sourceInfo = new SWFSourceInfo(null, swfFile, fileTitle); + OpenFileResult openResult = openFile(sourceInfo); + return openResult; + } catch (IOException ex) { + View.showMessageDialog(null, AppStrings.translate("open.error.cannotOpen"), AppStrings.translate("open.error"), JOptionPane.ERROR_MESSAGE); + return OpenFileResult.ERROR; + } + } + + public static OpenFileResult openFile(SWFSourceInfo sourceInfo) { + return openFile(new SWFSourceInfo[]{sourceInfo}); + } + + public static OpenFileResult openFile(SWFSourceInfo sourceInfo, Runnable executeAfterOpen) { + return openFile(new SWFSourceInfo[]{sourceInfo}, executeAfterOpen); + } + + public static OpenFileResult openFile(SWFSourceInfo sourceInfo, Runnable executeAfterOpen, int reloadIndex) { + return openFile(new SWFSourceInfo[]{sourceInfo}, executeAfterOpen, new int[]{reloadIndex}); + } + + public static OpenFileResult openFile(SWFSourceInfo[] newSourceInfos) { + return openFile(newSourceInfos, null); + } + + public static OpenFileResult openFile(SWFSourceInfo[] newSourceInfos, Runnable executeAfterOpen) { + return openFile(newSourceInfos, executeAfterOpen, null); + } + + public static OpenFileResult openFile(SWFSourceInfo[] newSourceInfos, Runnable executeAfterOpen, int[] reloadIndices) { + if (mainFrame != null && !Configuration.openMultipleFiles.get()) { + sourceInfos.clear(); + mainFrame.getPanel().closeAll(false); + mainFrame.setVisible(false); + Helper.freeMem(); + reloadIndices = null; + } + + loadingDialog.setVisible(true); + + OpenFileWorker wrk = new OpenFileWorker(newSourceInfos, executeAfterOpen, reloadIndices); + wrk.execute(); + if (reloadIndices == null) { + sourceInfos.addAll(Arrays.asList(newSourceInfos)); + } else { + for (int i = 0; i < reloadIndices.length; i++) { + sourceInfos.set(reloadIndices[i], newSourceInfos[i]); + } + } + return OpenFileResult.OK; + } + + public static void closeFile(SWFList swf) { + sourceInfos.remove(swf.sourceInfo); + mainFrame.getPanel().close(swf); + } + + public static void reloadFile(SWFList swf) { + //mainFrame.getPanel().close(swf); + openFile(swf.sourceInfo, null, sourceInfos.indexOf(swf.sourceInfo)); + } + + public static boolean closeAll() { + boolean closeResult = mainFrame.getPanel().closeAll(true); + if (closeResult) { + sourceInfos.clear(); + } + + return closeResult; + } + + public static boolean saveFileDialog(SWF swf, final SaveFileMode mode) { + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(Configuration.lastSaveDir.get())); + String ext = ".swf"; + switch (mode) { + case SAVE: + case SAVEAS: + if (swf.getFile() != null) { + ext = Path.getExtension(swf.getFile()); + } + break; + case EXE: + ext = ".exe"; + break; + } + + FileFilter swfFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".swf")) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.swf"); + } + }; + + FileFilter gfxFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".gfx")) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.gfx"); + } + }; + + ExeExportMode exeExportMode = null; + if (mode == SaveFileMode.EXE) { + exeExportMode = Configuration.exeExportMode.get(); + if (exeExportMode == null) { + exeExportMode = ExeExportMode.WRAPPER; + } + String filterDescription = null; + switch (exeExportMode) { + case WRAPPER: + case PROJECTOR_WIN: + ext = ".exe"; + filterDescription = "filter.exe"; + break; + case PROJECTOR_MAC: + ext = ".dmg"; + filterDescription = "filter.dmg"; + break; + case PROJECTOR_LINUX: + // linux projector is compressed with tar.gz + // todo: decompress + ext = ""; + filterDescription = "filter.linuxExe"; + break; + } + + String fext = ext; + String ffilterDescription = filterDescription; + FileFilter exeFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(fext)) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate(ffilterDescription); + } + }; + fc.setFileFilter(exeFilter); + } else if (swf.gfx) { + fc.addChoosableFileFilter(swfFilter); + fc.setFileFilter(gfxFilter); + } else { + fc.setFileFilter(swfFilter); + fc.addChoosableFileFilter(gfxFilter); + } + final String extension = ext; + fc.setAcceptAllFileFilterUsed(false); + JFrame f = new JFrame(); + View.setWindowIcon(f); + if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) { + File file = Helper.fixDialogFile(fc.getSelectedFile()); + FileFilter selFilter = fc.getFileFilter(); + try { + String fileName = file.getAbsolutePath(); + if (selFilter == swfFilter) { + if (!fileName.toLowerCase().endsWith(extension)) { + fileName += extension; + } + swf.gfx = false; + } + if (selFilter == gfxFilter) { + if (!fileName.toLowerCase().endsWith(".gfx")) { + fileName += ".gfx"; + } + swf.gfx = true; + } + Main.saveFile(swf, fileName, mode, exeExportMode); + Configuration.lastSaveDir.set(file.getParentFile().getAbsolutePath()); + return true; + } catch (IOException ex) { + View.showMessageDialog(null, AppStrings.translate("error.file.write")); + } + } + return false; + } + + public static boolean openFileDialog() { + JFileChooser fc = new JFileChooser(); + if (Configuration.openMultipleFiles.get()) { + fc.setMultiSelectionEnabled(true); + } + fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); + FileFilter allSupportedFilter = new FileFilter() { + private final String[] supportedExtensions = new String[]{".swf", ".gfx", ".swc", ".zip"}; + + @Override + public boolean accept(File f) { + String name = f.getName().toLowerCase(); + for (String ext : supportedExtensions) { + if (name.endsWith(ext)) { + return true; + } + } + return f.isDirectory(); + } + + @Override + public String getDescription() { + String exts = Helper.joinStrings(supportedExtensions, "*%s", "; "); + return AppStrings.translate("filter.supported") + " (" + exts + ")"; + } + }; + fc.setFileFilter(allSupportedFilter); + FileFilter swfFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".swf")) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.swf"); + } + }; + fc.addChoosableFileFilter(swfFilter); + + FileFilter swcFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".swc")) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.swc"); + } + }; + fc.addChoosableFileFilter(swcFilter); + + FileFilter gfxFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".gfx")) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.gfx"); + } + }; + fc.addChoosableFileFilter(gfxFilter); + + FileFilter zipFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".zip")) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.zip"); + } + }; + fc.addChoosableFileFilter(zipFilter); + + FileFilter binaryFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return true; + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.binary"); + } + }; + fc.addChoosableFileFilter(binaryFilter); + + fc.setAcceptAllFileFilterUsed(false); + JFrame f = new JFrame(); + View.setWindowIcon(f); + int returnVal = fc.showOpenDialog(f); + if (returnVal == JFileChooser.APPROVE_OPTION) { + Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); + File[] selFiles = fc.getSelectedFiles(); + for (File file : selFiles) { + File selfile = Helper.fixDialogFile(file); + Main.openFile(selfile.getAbsolutePath(), null); + } + return true; + } else { + return false; + } + } + + public static void displayErrorFrame() { + ErrorLogFrame.getInstance().setVisible(true); + } + + private static String md5(byte data[]) { + try { + java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5"); + byte[] array = md.digest(data); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < array.length; ++i) { + sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1, 3)); + } + return sb.toString(); + } catch (java.security.NoSuchAlgorithmException e) { + } + return null; + } + + private static void initGui() { + if (GraphicsEnvironment.isHeadless()) { + System.err.println("Error: Your system does not support Graphic User Interface"); + exit(); + } + + System.setProperty("sun.java2d.d3d", "false"); + System.setProperty("sun.java2d.noddraw", "true"); + + if (Configuration.hwAcceleratedGraphics.get()) { + System.setProperty("sun.java2d.opengl", Configuration._debugMode.get() ? "True" : "true"); + } else { + System.setProperty("sun.java2d.opengl", "false"); + } + + initUiLang(); + + if (Configuration.useRibbonInterface.get()) { + View.setLookAndFeel(); + } else { + try { + UIManager.put(SubstanceLookAndFeel.COLORIZATION_FACTOR, null); + UIManager.put("Tree.expandedIcon", null); + UIManager.put("Tree.collapsedIcon", null); + UIManager.put("ColorChooserUI", null); + UIManager.put("ColorChooser.swatchesRecentSwatchSize", null); + UIManager.put("ColorChooser.swatchesSwatchSize", null); + UIManager.put("RibbonApplicationMenuPopupPanelUI", null); + UIManager.put("RibbonApplicationMenuButtonUI", null); + UIManager.put("ProgressBarUI", null); + UIManager.put("TextField.background", null); + UIManager.put("FormattedTextField.background", null); + UIManager.put("CommandButtonUI", null); + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + View.execInEventDispatch(() -> { + ErrorLogFrame.createNewInstance(); + + autoCheckForUpdates(); + offerAssociation(); + loadingDialog = new LoadingDialog(); + + DebuggerTools.initDebugger().addMessageListener(new DebugListener() { + @Override + public void onMessage(String clientId, String msg) { + } + + @Override + public void onLoaderURL(String clientId, String url) { + } + + @Override + public void onLoaderBytes(String clientId, byte[] data) { + String hash = md5(data); + for (SWFList sl : Main.getMainFrame().getPanel().getSwfs()) { + for (int s = 0; s < sl.size(); s++) { + String t = sl.get(s).getFileTitle(); + if (t == null) { + t = ""; + } + if (t.endsWith(":" + hash)) { //this one is already opened + return; + } + } + } + SWF swf = Main.getMainFrame().getPanel().getCurrentSwf(); + + String title = swf == null ? "?" : swf.getFileTitle(); + title = title + ":" + hash; + String tfile; + try { + tfile = tempFile(title); + Helper.writeFile(tfile, data); + openFile(new SWFSourceInfo(null, tfile, title)); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Cannot create tempfile"); + } + } + + @Override + public void onFinish(String clientId) { + } + }); + + try { + flashDebugger = new Debugger(); + debugHandler = new DebuggerHandler(); + debugHandler.addBreakListener(new DebuggerHandler.BreakListener() { + @Override + public void doContinue() { + mainFrame.getPanel().clearDebuggerColors(); + } + + @Override + public void breakAt(String scriptName, int line, final int classIndex, final int traitIndex, final int methodIndex) { + View.execInEventDispatch(new Runnable() { + @Override + public void run() { + mainFrame.getPanel().gotoScriptLine(getMainFrame().getPanel().getCurrentSwf(), scriptName, line, classIndex, traitIndex, methodIndex); + } + }); + } + }); + debugHandler.addConnectionListener(new DebuggerHandler.ConnectionListener() { + @Override + public void connected() { + Main.mainFrame.getMenu().updateComponents(); + } + + @Override + public void disconnected() { + if (Main.mainFrame != null && Main.mainFrame.getPanel() != null) { + Main.mainFrame.getPanel().refreshBreakPoints(); + } + } + }); + flashDebugger.addConnectionListener(debugHandler); + } catch (IOException ex) { + logger.log(Level.SEVERE, "eeex", ex); + } + }); + } + + public static void startDebugger() { + flashDebugger.startDebugger(); + } + + public static void stopDebugger() { + flashDebugger.stopDebugger(); + } + + public static void showModeFrame() { + ensureMainFrame(); + mainFrame.setVisible(true); + } + + private static void offerAssociation() { + boolean offered = Configuration.offeredAssociation.get(); + if (!offered) { + if (Platform.isWindows()) { + if ((!ContextMenuTools.isAddedToContextMenu()) && View.showConfirmDialog(null, "Do you want to add FFDec to context menu of SWF files?\n(Can be changed later from main menu)", "Context menu", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { + ContextMenuTools.addToContextMenu(true, false); + } + } + + Configuration.offeredAssociation.set(true); + } + } + + public static void initUiLang() { + if (GraphicsEnvironment.isHeadless()) { //No GUI in OS + return; + } + try { + Class cl = Class.forName("org.pushingpixels.substance.api.SubstanceLookAndFeel"); + Field field = cl.getDeclaredField("LABEL_BUNDLE"); + field.setAccessible(true); + field.set(null, null); + } catch (Throwable ex) { + logger.log(Level.SEVERE, null, ex); + } + + UIManager.put("OptionPane.okButtonText", AppStrings.translate("button.ok")); + UIManager.put("OptionPane.yesButtonText", AppStrings.translate("button.yes")); + UIManager.put("OptionPane.noButtonText", AppStrings.translate("button.no")); + UIManager.put("OptionPane.cancelButtonText", AppStrings.translate("button.cancel")); + UIManager.put("OptionPane.messageDialogTitle", AppStrings.translate("dialog.message.title")); + UIManager.put("OptionPane.titleText", AppStrings.translate("dialog.select.title")); + + UIManager.put("FileChooser.acceptAllFileFilterText", AppStrings.translate("FileChooser.acceptAllFileFilterText")); + UIManager.put("FileChooser.lookInLabelText", AppStrings.translate("FileChooser.lookInLabelText")); + UIManager.put("FileChooser.cancelButtonText", AppStrings.translate("button.cancel")); + UIManager.put("FileChooser.cancelButtonToolTipText", AppStrings.translate("button.cancel")); + UIManager.put("FileChooser.openButtonText", AppStrings.translate("FileChooser.openButtonText")); + UIManager.put("FileChooser.openButtonToolTipText", AppStrings.translate("FileChooser.openButtonToolTipText")); + UIManager.put("FileChooser.filesOfTypeLabelText", AppStrings.translate("FileChooser.filesOfTypeLabelText")); + UIManager.put("FileChooser.fileNameLabelText", AppStrings.translate("FileChooser.fileNameLabelText")); + UIManager.put("FileChooser.listViewButtonToolTipText", AppStrings.translate("FileChooser.listViewButtonToolTipText")); + UIManager.put("FileChooser.listViewButtonAccessibleName", AppStrings.translate("FileChooser.listViewButtonAccessibleName")); + UIManager.put("FileChooser.detailsViewButtonToolTipText", AppStrings.translate("FileChooser.detailsViewButtonToolTipText")); + UIManager.put("FileChooser.detailsViewButtonAccessibleName", AppStrings.translate("FileChooser.detailsViewButtonAccessibleName")); + UIManager.put("FileChooser.upFolderToolTipText", AppStrings.translate("FileChooser.upFolderToolTipText")); + UIManager.put("FileChooser.upFolderAccessibleName", AppStrings.translate("FileChooser.upFolderAccessibleName")); + UIManager.put("FileChooser.homeFolderToolTipText", AppStrings.translate("FileChooser.homeFolderToolTipText")); + UIManager.put("FileChooser.homeFolderAccessibleName", AppStrings.translate("FileChooser.homeFolderAccessibleName")); + UIManager.put("FileChooser.fileNameHeaderText", AppStrings.translate("FileChooser.fileNameHeaderText")); + UIManager.put("FileChooser.fileSizeHeaderText", AppStrings.translate("FileChooser.fileSizeHeaderText")); + UIManager.put("FileChooser.fileTypeHeaderText", AppStrings.translate("FileChooser.fileTypeHeaderText")); + UIManager.put("FileChooser.fileDateHeaderText", AppStrings.translate("FileChooser.fileDateHeaderText")); + UIManager.put("FileChooser.fileAttrHeaderText", AppStrings.translate("FileChooser.fileAttrHeaderText")); + UIManager.put("FileChooser.openDialogTitleText", AppStrings.translate("FileChooser.openDialogTitleText")); + UIManager.put("FileChooser.directoryDescriptionText", AppStrings.translate("FileChooser.directoryDescriptionText")); + UIManager.put("FileChooser.directoryOpenButtonText", AppStrings.translate("FileChooser.directoryOpenButtonText")); + UIManager.put("FileChooser.directoryOpenButtonToolTipText", AppStrings.translate("FileChooser.directoryOpenButtonToolTipText")); + UIManager.put("FileChooser.fileDescriptionText", AppStrings.translate("FileChooser.fileDescriptionText")); + UIManager.put("FileChooser.fileNameLabelText", AppStrings.translate("FileChooser.fileNameLabelText")); + UIManager.put("FileChooser.helpButtonText", AppStrings.translate("FileChooser.helpButtonText")); + UIManager.put("FileChooser.helpButtonToolTipText", AppStrings.translate("FileChooser.helpButtonToolTipText")); + UIManager.put("FileChooser.newFolderAccessibleName", AppStrings.translate("FileChooser.newFolderAccessibleName")); + UIManager.put("FileChooser.newFolderErrorText", AppStrings.translate("FileChooser.newFolderErrorText")); + UIManager.put("FileChooser.newFolderToolTipText", AppStrings.translate("FileChooser.newFolderToolTipText")); + UIManager.put("FileChooser.other.newFolder", AppStrings.translate("FileChooser.other.newFolder")); + UIManager.put("FileChooser.other.newFolder.subsequent", AppStrings.translate("FileChooser.other.newFolder.subsequent")); + UIManager.put("FileChooser.win32.newFolder", AppStrings.translate("FileChooser.win32.newFolder")); + UIManager.put("FileChooser.win32.newFolder.subsequent", AppStrings.translate("FileChooser.win32.newFolder.subsequent")); + UIManager.put("FileChooser.saveButtonText", AppStrings.translate("FileChooser.saveButtonText")); + UIManager.put("FileChooser.saveButtonToolTipText", AppStrings.translate("FileChooser.saveButtonToolTipText")); + UIManager.put("FileChooser.saveDialogTitleText", AppStrings.translate("FileChooser.saveDialogTitleText")); + UIManager.put("FileChooser.saveInLabelText", AppStrings.translate("FileChooser.saveInLabelText")); + UIManager.put("FileChooser.updateButtonText", AppStrings.translate("FileChooser.updateButtonText")); + UIManager.put("FileChooser.updateButtonToolTipText", AppStrings.translate("FileChooser.updateButtonToolTipText")); + + UIManager.put("FileChooser.detailsViewActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.detailsViewActionLabel.textAndMnemonic")); + UIManager.put("FileChooser.detailsViewButtonToolTip.textAndMnemonic", AppStrings.translate("FileChooser.detailsViewButtonToolTip.textAndMnemonic")); + UIManager.put("FileChooser.fileAttrHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileAttrHeader.textAndMnemonic")); + UIManager.put("FileChooser.fileDateHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileDateHeader.textAndMnemonic")); + UIManager.put("FileChooser.fileNameHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileNameHeader.textAndMnemonic")); + UIManager.put("FileChooser.fileNameLabel.textAndMnemonic", AppStrings.translate("FileChooser.fileNameLabel.textAndMnemonic")); + UIManager.put("FileChooser.fileSizeHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileSizeHeader.textAndMnemonic")); + UIManager.put("FileChooser.fileTypeHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileTypeHeader.textAndMnemonic")); + UIManager.put("FileChooser.filesOfTypeLabel.textAndMnemonic", AppStrings.translate("FileChooser.filesOfTypeLabel.textAndMnemonic")); + UIManager.put("FileChooser.folderNameLabel.textAndMnemonic", AppStrings.translate("FileChooser.folderNameLabel.textAndMnemonic")); + UIManager.put("FileChooser.homeFolderToolTip.textAndMnemonic", AppStrings.translate("FileChooser.homeFolderToolTip.textAndMnemonic")); + UIManager.put("FileChooser.listViewActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.listViewActionLabel.textAndMnemonic")); + UIManager.put("FileChooser.listViewButtonToolTip.textAndMnemonic", AppStrings.translate("FileChooser.listViewButtonToolTip.textAndMnemonic")); + UIManager.put("FileChooser.lookInLabel.textAndMnemonic", AppStrings.translate("FileChooser.lookInLabel.textAndMnemonic")); + UIManager.put("FileChooser.newFolderActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.newFolderActionLabel.textAndMnemonic")); + UIManager.put("FileChooser.newFolderToolTip.textAndMnemonic", AppStrings.translate("FileChooser.newFolderToolTip.textAndMnemonic")); + UIManager.put("FileChooser.refreshActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.refreshActionLabel.textAndMnemonic")); + UIManager.put("FileChooser.saveInLabel.textAndMnemonic", AppStrings.translate("FileChooser.saveInLabel.textAndMnemonic")); + UIManager.put("FileChooser.upFolderToolTip.textAndMnemonic", AppStrings.translate("FileChooser.upFolderToolTip.textAndMnemonic")); + UIManager.put("FileChooser.viewMenuButtonAccessibleName", AppStrings.translate("FileChooser.viewMenuButtonAccessibleName")); + UIManager.put("FileChooser.viewMenuButtonToolTipText", AppStrings.translate("FileChooser.viewMenuButtonToolTipText")); + UIManager.put("FileChooser.viewMenuLabel.textAndMnemonic", AppStrings.translate("FileChooser.viewMenuLabel.textAndMnemonic")); + UIManager.put("FileChooser.newFolderActionLabelText", AppStrings.translate("FileChooser.newFolderActionLabelText")); + UIManager.put("FileChooser.listViewActionLabelText", AppStrings.translate("FileChooser.listViewActionLabelText")); + UIManager.put("FileChooser.detailsViewActionLabelText", AppStrings.translate("FileChooser.detailsViewActionLabelText")); + UIManager.put("FileChooser.refreshActionLabelText", AppStrings.translate("FileChooser.refreshActionLabelText")); + UIManager.put("FileChooser.sortMenuLabelText", AppStrings.translate("FileChooser.sortMenuLabelText")); + UIManager.put("FileChooser.viewMenuLabelText", AppStrings.translate("FileChooser.viewMenuLabelText")); + UIManager.put("FileChooser.fileSizeKiloBytes", AppStrings.translate("FileChooser.fileSizeKiloBytes")); + UIManager.put("FileChooser.fileSizeMegaBytes", AppStrings.translate("FileChooser.fileSizeMegaBytes")); + UIManager.put("FileChooser.fileSizeGigaBytes", AppStrings.translate("FileChooser.fileSizeGigaBytes")); + UIManager.put("FileChooser.folderNameLabelText", AppStrings.translate("FileChooser.folderNameLabelText")); + + UIManager.put("ColorChooser.okText", AppStrings.translate("ColorChooser.okText")); + UIManager.put("ColorChooser.cancelText", AppStrings.translate("ColorChooser.cancelText")); + UIManager.put("ColorChooser.resetText", AppStrings.translate("ColorChooser.resetText")); + UIManager.put("ColorChooser.previewText", AppStrings.translate("ColorChooser.previewText")); + UIManager.put("ColorChooser.swatchesNameText", AppStrings.translate("ColorChooser.swatchesNameText")); + UIManager.put("ColorChooser.swatchesRecentText", AppStrings.translate("ColorChooser.swatchesRecentText")); + UIManager.put("ColorChooser.sampleText", AppStrings.translate("ColorChooser.sampleText")); + + } + + public static void initLang() { + if (!Configuration.locale.hasValue()) { + if (Platform.isWindows()) { + //Load from Installer + String uninstKey = "{E618D276-6596-41F4-8A98-447D442A77DB}_is1"; + uninstKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + uninstKey; + try { + if (Advapi32Util.registryKeyExists(WinReg.HKEY_LOCAL_MACHINE, uninstKey)) { + if (Advapi32Util.registryValueExists(WinReg.HKEY_LOCAL_MACHINE, uninstKey, "NSIS: Language")) { + String installedLoc = Advapi32Util.registryGetStringValue(WinReg.HKEY_LOCAL_MACHINE, uninstKey, "NSIS: Language"); + int lcid = Integer.parseInt(installedLoc); + char[] buf = new char[9]; + int cnt = Kernel32.INSTANCE.GetLocaleInfo(lcid, Kernel32.LOCALE_SISO639LANGNAME, buf, 9); + String langCode = new String(buf, 0, cnt).trim().toLowerCase(); + + cnt = Kernel32.INSTANCE.GetLocaleInfo(lcid, Kernel32.LOCALE_SISO3166CTRYNAME, buf, 9); + String countryCode = new String(buf, 0, cnt).trim().toLowerCase(); + + List langs = Arrays.asList(SelectLanguageDialog.getAvailableLanguages()); + for (int i = 0; i < langs.size(); i++) { + langs.set(i, langs.get(i).toLowerCase()); + } + + String selectedLang = null; + + if (langs.contains(langCode + "-" + countryCode)) { + selectedLang = SelectLanguageDialog.getAvailableLanguages()[langs.indexOf(langCode + "-" + countryCode)]; + } else if (langs.contains(langCode)) { + selectedLang = SelectLanguageDialog.getAvailableLanguages()[langs.indexOf(langCode)]; + } + if (selectedLang != null) { + Configuration.locale.set(selectedLang); + } + } + } + } catch (Exception ex) { + //ignore + } + } + } + Locale.setDefault(Locale.forLanguageTag(Configuration.locale.get())); + AppStrings.updateLanguage(); + + Helper.decompilationErrorAdd = AppStrings.translate(Configuration.autoDeobfuscate.get() ? "deobfuscation.comment.failed" : "deobfuscation.comment.tryenable"); + } + + /** + * Clear old FFDec/JavactiveX temp files + */ + private static void clearTemp() { + String tempDirPath = System.getProperty("java.io.tmpdir"); + if (tempDirPath == null) { + return; + } + File tempDir = new File(tempDirPath); + if (!tempDir.exists()) { + return; + } + File[] delFiles = tempDir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.matches("ffdec_cache.*\\.tmp") || name.matches("javactivex_.*\\.exe") || name.matches("temp[0-9]+\\.swf") || name.matches("ffdec_view_.*\\.swf"); + } + }); + + if (delFiles != null) { + for (File f : delFiles) { + try { + f.delete(); + } catch (Exception ex) { + //ignore + } + } + } + } + + /** + * @param args the command line arguments + * @throws IOException On error + */ + public static void main(String[] args) throws IOException { + setSessionLoaded(false); + clearTemp(); + + try { + SWFDecompilerPlugin.loadPlugins(); + } catch (Throwable ex) { + logger.log(Level.SEVERE, "Failed to load plugins", ex); + } + + AppStrings.setResourceClass(MainFrame.class); + initLogging(Configuration._debugMode.get()); + + initLang(); + + if (Configuration.cacheOnDisk.get()) { + Cache.setStorageType(Cache.STORAGE_FILES); + } else { + Cache.setStorageType(Cache.STORAGE_MEMORY); + } + + if (args.length == 0) { + initGui(); + View.execInEventDispatch(() -> { + if (Configuration.allowOnlyOneInstance.get() && FirstInstance.focus()) { //Try to focus first instance + Main.exit(); + } else { + showModeFrame(); + reloadLastSession(); + } + }); + } else { + setSessionLoaded(true); + String[] filesToOpen = CommandLineArgumentParser.parseArguments(args); + if (filesToOpen != null && filesToOpen.length > 0) { + View.execInEventDispatch(() -> { + initGui(); + shouldCloseWhenClosingLoadingDialog = true; + if (Configuration.allowOnlyOneInstance.get() && FirstInstance.openFiles(Arrays.asList(filesToOpen))) { //Try to open in first instance + Main.exit(); + } else { + for (String fileToOpen : filesToOpen) { + openFile(fileToOpen, null); + } + } + }); + } + } + } + + private static void reloadLastSession() { + boolean openingFiles = false; + if (Configuration.saveSessionOnExit.get()) { + String lastSession = Configuration.lastSessionFiles.get(); + if (lastSession != null && lastSession.length() > 0) { + String[] filesToOpen = lastSession.split(File.pathSeparator, -1); + List exfiles = new ArrayList<>(); + List extitles = new ArrayList<>(); + String lastSessionTitles = Configuration.lastSessionFileTitles.get(); + String[] fileTitles = new String[0]; + if (lastSessionTitles != null && !lastSessionTitles.isEmpty()) { + fileTitles = lastSessionTitles.split(File.pathSeparator, -1); + } + for (int i = 0; i < filesToOpen.length; i++) { + if (new File(filesToOpen[i]).exists()) { + exfiles.add(filesToOpen[i]); + if (fileTitles.length > i) { + extitles.add(fileTitles[i]); + } else { + extitles.add(null); + } + } + } + SWFSourceInfo[] sourceInfos = new SWFSourceInfo[exfiles.size()]; + for (int i = 0; i < exfiles.size(); i++) { + String extitle = extitles.get(i); + sourceInfos[i] = new SWFSourceInfo(null, exfiles.get(i), extitle == null || extitle.isEmpty() ? null : extitle); + } + if (sourceInfos.length > 0) { + openingFiles = true; + openFile(sourceInfos, () -> { + mainFrame.getPanel().tagTree.setSelectionPathString(Configuration.lastSessionSelection.get()); + setSessionLoaded(true); + }); + } + } + } + + if (!openingFiles) { + setSessionLoaded(true); + } + } + + public static String tempFile(String url) throws IOException { + File f = new File(Configuration.getFFDecHome() + "saved" + File.separator); + Path.createDirectorySafe(f); + return Configuration.getFFDecHome() + "saved" + File.separator + "asdec_" + Integer.toHexString(url.hashCode()) + ".tmp"; + } + + public static void removeTrayIcon() { + if (SystemTray.isSupported()) { + SystemTray tray = SystemTray.getSystemTray(); + if (trayIcon != null) { + tray.remove(trayIcon); + trayIcon = null; + } + } + } + + public static void switchProxy() { + proxyFrame.switchState(); + if (stopMenuItem != null) { + if (proxyFrame.isRunning()) { + stopMenuItem.setLabel(AppStrings.translate("proxy.stop")); + } else { + stopMenuItem.setLabel(AppStrings.translate("proxy.start")); + } + } + } + + public static void addTrayIcon() { + if (trayIcon != null) { + return; + } + if (SystemTray.isSupported()) { + SystemTray tray = SystemTray.getSystemTray(); + trayIcon = new TrayIcon(View.loadImage("proxy16"), ApplicationInfo.VENDOR + " " + ApplicationInfo.SHORT_APPLICATION_NAME + " " + AppStrings.translate("proxy")); + trayIcon.setImageAutoSize(true); + PopupMenu trayPopup = new PopupMenu(); + + ActionListener trayListener = new ActionListener() { + /** + * Invoked when an action occurs. + */ + @Override + public void actionPerformed(ActionEvent e) { + if (e.getActionCommand().equals("EXIT")) { + Main.exit(); + } + if (e.getActionCommand().equals("SHOW")) { + Main.showProxy(); + } + if (e.getActionCommand().equals("SWITCH")) { + Main.switchProxy(); + } + } + }; + + MenuItem showMenuItem = new MenuItem(AppStrings.translate("proxy.show")); + showMenuItem.setActionCommand("SHOW"); + showMenuItem.addActionListener(trayListener); + trayPopup.add(showMenuItem); + stopMenuItem = new MenuItem(AppStrings.translate("proxy.start")); + stopMenuItem.setActionCommand("SWITCH"); + stopMenuItem.addActionListener(trayListener); + trayPopup.add(stopMenuItem); + trayPopup.addSeparator(); + MenuItem exitMenuItem = new MenuItem(AppStrings.translate("exit")); + exitMenuItem.setActionCommand("EXIT"); + exitMenuItem.addActionListener(trayListener); + trayPopup.add(exitMenuItem); + + trayIcon.setPopupMenu(trayPopup); + trayIcon.addMouseListener(new MouseAdapter() { + /** + * {@inheritDoc} + */ + @Override + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + Main.showProxy(); + } + } + }); + try { + tray.add(trayIcon); + } catch (AWTException ex) { + } + } + } + + public static void exit() { + Configuration.saveConfig(); + if (mainFrame != null && mainFrame.getPanel() != null) { + mainFrame.getPanel().unloadFlashPlayer(); + mainFrame.dispose(); + } + if (fileTxt != null) { + try { + fileTxt.flush(); + fileTxt.close(); + } catch (Exception ex) { + //ignore + } + } + System.exit(0); + } + + public static void about() { + (new AboutDialog()).setVisible(true); + } + + public static void advancedSettings() { + advancedSettings(null); + } + + public static void advancedSettings(String category) { + (new AdvancedSettingsDialog(category)).setVisible(true); + } + + public static void autoCheckForUpdates() { + if (Configuration.checkForUpdatesAuto.get()) { + Calendar lastUpdatesCheckDate = Configuration.lastUpdatesCheckDate.get(); + if ((lastUpdatesCheckDate == null) || (lastUpdatesCheckDate.getTime().getTime() < Calendar.getInstance().getTime().getTime() - Configuration.checkForUpdatesDelay.get())) { + new SwingWorker() { + @Override + protected Object doInBackground() throws Exception { + checkForUpdates(); + return null; + } + }.execute(); + } + } + } + + public static boolean checkForUpdates() { + String currentVersion = ApplicationInfo.version; + if (currentVersion.equals("unknown")) { + // sometimes during development the version information is not available + return false; + } + + List accepted = new ArrayList<>(); + if (Configuration.checkForUpdatesStable.get()) { + accepted.add("stable"); + } + if (Configuration.checkForUpdatesNightly.get()) { + accepted.add("nightly"); + } + + if (accepted.isEmpty()) { + return false; + } + + String acceptVersions = String.join(",", accepted); + try { + String proxyAddress = Configuration.updateProxyAddress.get(); + URL url = new URL(ApplicationInfo.updateCheckUrl); + + URLConnection uc; + if (proxyAddress != null && !proxyAddress.isEmpty()) { + int port = 8080; + if (proxyAddress.contains(":")) { + String[] parts = proxyAddress.split(":"); + port = Integer.parseInt(parts[1]); + proxyAddress = parts[0]; + } + + uc = url.openConnection(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyAddress, port))); + } else { + uc = url.openConnection(); + } + uc.setRequestProperty("X-Accept-Versions", acceptVersions); + uc.setRequestProperty("X-Update-Major", "" + UPDATE_SYSTEM_MAJOR); + uc.setRequestProperty("X-Update-Minor", "" + UPDATE_SYSTEM_MINOR); + uc.setRequestProperty("User-Agent", ApplicationInfo.shortApplicationVerName); + String currentLoc = Configuration.locale.get("en"); + uc.setRequestProperty("Accept-Language", currentLoc + ("en".equals(currentLoc) ? "" : ", en;q=0.8")); + + uc.connect(); + + BufferedReader br = new BufferedReader(new InputStreamReader(uc.getInputStream())); + String s; + final java.util.List versions = new ArrayList<>(); + String header = ""; + Pattern headerPat = Pattern.compile("\\[([a-zA-Z0-9]+)\\]"); + int updateMajor; + int updateMinor; + Version ver = null; + while ((s = br.readLine()) != null) { + + Matcher m = headerPat.matcher(s); + if (m.matches()) { + header = m.group(1); + if (header.equals("version")) { + ver = new Version(); + versions.add(ver); + } + if (header.equals("noversion")) { + break; + } + } else if (s.contains("=")) { + String key = s.substring(0, s.indexOf('=')); + String val = s.substring(s.indexOf('=') + 1); + if ("updateSystem".equals(header)) { + if (key.equals("majorVersion")) { + updateMajor = Integer.parseInt(val); + if (updateMajor > UPDATE_SYSTEM_MAJOR) { + break; + } + } + if (key.equals("minorVersion")) { + updateMinor = Integer.parseInt(val); + } + } + if ("version".equals(header) && (ver != null)) { + if (key.equals("versionId")) { + ver.versionId = Integer.parseInt(val); + } + if (key.equals("versionName")) { + ver.versionName = val; + } + if (key.equals("nightly")) { + ver.nightly = val.equals("true"); + } + if (key.equals("revision")) { + ver.revision = val; + } + if (key.equals("build")) { + ver.build = Integer.parseInt(val); + } + if (key.equals("major")) { + ver.major = Integer.parseInt(val); + } + if (key.equals("minor")) { + ver.minor = Integer.parseInt(val); + } + if (key.equals("release")) { + ver.release = Integer.parseInt(val); + } + if (key.equals("longVersionName")) { + ver.longVersionName = val; + } + if (key.equals("releaseDate")) { + ver.releaseDate = val; + } + if (key.equals("appName")) { + ver.appName = val; + } + if (key.equals("appFullName")) { + ver.appFullName = val; + } + if (key.equals("updateLink")) { + ver.updateLink = val; + } + if (key.equals("change[]")) { + String changeType = val.substring(0, val.indexOf('|')); + String change = val.substring(val.indexOf('|') + 1); + if (!ver.changes.containsKey(changeType)) { + ver.changes.put(changeType, new ArrayList<>()); + } + List chlist = ver.changes.get(changeType); + chlist.add(change); + } + } + } + } + + if (!versions.isEmpty()) { + View.execInEventDispatch(() -> { + NewVersionDialog newVersionDialog = new NewVersionDialog(versions); + newVersionDialog.setVisible(true); + Configuration.lastUpdatesCheckDate.set(Calendar.getInstance()); + }); + + return true; + } + } catch (IOException | NumberFormatException ex) { + return false; + } + Configuration.lastUpdatesCheckDate.set(Calendar.getInstance()); + return false; + } + + private static FileHandler fileTxt; + + public static void clearLogFile() { + Logger logger = Logger.getLogger(""); + + FileHandler oldFileTxt = fileTxt; + fileTxt = null; + if (oldFileTxt != null) { + logger.removeHandler(fileTxt); + oldFileTxt.flush(); + oldFileTxt.close(); + } + + String fileName = null; + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss"); + + try { + fileName = Configuration.getFFDecHome() + "logs" + File.separator; + if (Configuration.useDetailedLogging.get()) { + fileName += "log-" + sdf.format(new Date()) + ".txt"; + } else { + fileName += "log.txt"; + } + File f = new File(fileName).getParentFile(); + if (!f.exists()) { + f.mkdir(); + } + fileTxt = new FileHandler(fileName); + } catch (IOException | SecurityException ex) { + //cannot get lock error + if (ex.getMessage().contains("lock for")) { + //remove all old log files and their .lck + for (int i = 0; i <= 100; i++) { + File flog = new File(fileName + (i == 0 ? "" : "." + i)); + File flog_lock = new File(fileName + (i == 0 ? "" : "." + i) + ".lck"); + flog.delete(); + flog_lock.delete(); + } + try { + fileTxt = new FileHandler(fileName); + } catch (IOException | SecurityException ex1) { + logger.log(Level.SEVERE, "Cannot initialize logging", ex); + } + } else { + logger.log(Level.SEVERE, "Cannot initialize logging", ex); + } + } + + Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread t, Throwable e) { + logger.log(Level.SEVERE, "Uncaught exception in thread: " + t.getName(), e); + if (e instanceof OutOfMemoryError || !Helper.is64BitJre() && Helper.is64BitOs()) { + View.showMessageDialog(null, AppStrings.translate("message.warning.outOfMemory32BitJre"), AppStrings.translate("message.warning"), JOptionPane.WARNING_MESSAGE); + } + } + }); + + Formatter formatterTxt = new LogFormatter(); + if (fileTxt != null) { + fileTxt.setFormatter(formatterTxt); + logger.addHandler(fileTxt); + } + + if (!GraphicsEnvironment.isHeadless() && ErrorLogFrame.hasInstance()) { + ErrorLogFrame.getInstance().clearErrorState(); + } + + sdf = new SimpleDateFormat("yyyy-MM-dd"); + logger.log(Level.INFO, "Date: {0}", sdf.format(new Date())); + logger.log(Level.INFO, ApplicationInfo.applicationVerName); + logger.log(Level.INFO, "{0} {1} {2}", new Object[]{ + System.getProperty("os.name"), System.getProperty("os.version"), System.getProperty("os.arch")}); + logger.log(Level.INFO, "{0} {1} {2}", new Object[]{ + System.getProperty("java.version"), System.getProperty("java.vendor"), System.getProperty("os.arch")}); + } + + public static void initLogging(boolean debug) { + try { + Logger logger = Logger.getLogger(""); + logger.setLevel(Configuration.logLevel); + + Handler[] handlers = logger.getHandlers(); + for (int i = handlers.length - 1; i >= 0; i--) { + logger.removeHandler(handlers[i]); + } + + ConsoleHandler conHan = new ConsoleHandler(); + conHan.setLevel(debug ? Level.CONFIG : Level.WARNING); + SimpleFormatter formatterTxt = new SimpleFormatter(); + conHan.setFormatter(formatterTxt); + logger.addHandler(conHan); + clearLogFile(); + + } catch (Exception ex) { + throw new RuntimeException("Problems with creating the log files"); + } + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java index 28a3f9d6d..0b687bf5b 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java @@ -1,1306 +1,1306 @@ -/* - * Copyright (C) 2010-2016 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.debugger.flash.DebuggerCommands; -import com.jpexs.decompiler.flash.ApplicationInfo; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFBundle; -import com.jpexs.decompiler.flash.SWFSourceInfo; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.configuration.ConfigurationItemChangeListener; -import com.jpexs.decompiler.flash.console.ContextMenuTools; -import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools; -import com.jpexs.decompiler.flash.gui.helpers.CheckResources; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.helpers.ByteArrayRange; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.utf8.Utf8Helper; -import com.sun.jna.Platform; -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.KeyEventDispatcher; -import java.awt.KeyboardFocusManager; -import java.awt.ScrollPane; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.awt.event.WindowEvent; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.io.UnsupportedEncodingException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.AbstractButton; -import javax.swing.JComboBox; -import javax.swing.JDialog; -import javax.swing.JEditorPane; -import javax.swing.JFrame; -import javax.swing.JOptionPane; - -/** - * - * @author JPEXS - */ -public abstract class MainFrameMenu implements MenuBuilder { - - private final MainFrame mainFrame; - - private KeyEventDispatcher keyEventDispatcher; - - private SWF swf; - - private ConfigurationItemChangeListener configListenerAutoDeobfuscate; - - private ConfigurationItemChangeListener configListenerSimplifyExpressions; - - private ConfigurationItemChangeListener configListenerInternalFlashViewer; - - private ConfigurationItemChangeListener configListenerParallelSpeedUp; - - private ConfigurationItemChangeListener configListenerDecompile; - - //private ConfigurationItemChangeListener configListenerCacheOnDisk; - private ConfigurationItemChangeListener configListenerGotoMainClassOnStartup; - - private ConfigurationItemChangeListener configListenerAutoRenameIdentifiers; - - private ConfigurationItemChangeListener configListenerAutoOpenLoadedSWFs; - - protected final Map menuHotkeys = new HashMap<>(); - - @Override - public HotKey getMenuHotkey(String path) { - return menuHotkeys.get(path); - } - - protected final Map menuActions = new HashMap<>(); - - public boolean isInternalFlashViewerSelected() { - return isMenuChecked("/settings/internalViewer"); //miInternalViewer.isSelected(); - } - - private final boolean externalFlashPlayerUnavailable; - - public MainFrameMenu(MainFrame mainFrame, boolean externalFlashPlayerUnavailable) { - registerHotKeys(); - this.mainFrame = mainFrame; - this.externalFlashPlayerUnavailable = externalFlashPlayerUnavailable; - } - - protected String translate(String key) { - return mainFrame.translate(key); - } - - protected boolean openActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return false; - } - - Main.openFileDialog(); - return true; - } - - protected boolean saveActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return false; - } - - if (swf != null) { - boolean saved = false; - if (swf.swfList != null && swf.swfList.isBundle()) { - SWFBundle bundle = swf.swfList.bundle; - if (!bundle.isReadOnly()) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - swf.saveTo(baos); - saved = bundle.putSWF(swf.getFileTitle(), new ByteArrayInputStream(baos.toByteArray())); - } catch (IOException ex) { - Logger.getLogger(MainFrameMenu.class.getName()).log(Level.SEVERE, "Cannot save SWF", ex); - } - } - } else if (swf.binaryData != null) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - swf.saveTo(baos); - swf.binaryData.binaryData = new ByteArrayRange(baos.toByteArray()); - swf.binaryData.setModified(true); - saved = true; - } catch (IOException ex) { - Logger.getLogger(MainFrameMenu.class.getName()).log(Level.SEVERE, "Cannot save SWF", ex); - } - } else if (swf.getFile() == null) { - saved = saveAs(swf, SaveFileMode.SAVEAS); - } else { - try { - Main.saveFile(swf, swf.getFile()); - saved = true; - } catch (IOException ex) { - Logger.getLogger(MainFrameMenu.class.getName()).log(Level.SEVERE, null, ex); - View.showMessageDialog(null, translate("error.file.save"), translate("error"), JOptionPane.ERROR_MESSAGE); - } - } - if (saved) { - swf.clearModified(); - mainFrame.getPanel().refreshTree(swf); - } - - return true; - } - - return false; - } - - protected boolean saveAsActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return false; - } - - if (swf != null) { - if (saveAs(swf, SaveFileMode.SAVEAS)) { - swf.clearModified(); - } - - return true; - } - - return false; - } - - private boolean saveAs(SWF swf, SaveFileMode mode) { - if (Main.saveFileDialog(swf, mode)) { - mainFrame.setTitle(ApplicationInfo.applicationVerName + (Configuration.displayFileName.get() ? " - " + swf.getFileTitle() : "")); - updateComponents(swf); - return true; - } - return false; - } - - protected void saveAsExeActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - if (swf != null) { - saveAs(swf, SaveFileMode.EXE); - } - } - - protected void closeActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - if (swf == null) { - return; - } - - Main.closeFile(swf.swfList); - } - - protected boolean closeAllActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return false; - } - - if (swf != null) { - return Main.closeAll(); - } - - return false; - } - - protected void importTextActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - mainFrame.getPanel().importText(swf); - } - - protected void importScriptActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - mainFrame.getPanel().importScript(swf); - } - - protected void importSymbolClassActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - mainFrame.getPanel().importSymbolClass(swf); - } - - protected boolean exportAllActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return false; - } - - return export(false); - } - - protected boolean exportSelectedActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return false; - } - - return export(true); - } - - protected boolean export(boolean onlySelected) { - if (swf != null) { - mainFrame.getPanel().export(onlySelected); - return true; - } - - return false; - } - - protected void exportFlaActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - mainFrame.getPanel().exportFla(swf); - } - - protected void importXmlActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - mainFrame.getPanel().importSwfXml(); - } - - protected void exportXmlActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - mainFrame.getPanel().exportSwfXml(); - } - - protected boolean searchActionPerformed(ActionEvent evt) { - return search(evt, null); - } - - protected boolean searchInTextPerformed(ActionEvent evt) { - return search(evt, true); - } - - protected boolean searchInActionPerformed(ActionEvent evt) { - return search(evt, false); - } - - protected boolean search(ActionEvent evt, Boolean searchInText) { - if (swf != null) { - mainFrame.getPanel().searchInActionScriptOrText(searchInText, swf); - return true; - } - - return false; - } - - protected boolean replaceActionPerformed(ActionEvent evt) { - if (swf != null) { - mainFrame.getPanel().replaceText(); - return true; - } - - return false; - } - - protected void showProxyActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - Main.showProxy(); - } - - protected boolean clearLog(ActionEvent evt) { - ErrorLogFrame.getInstance().clearLog(); - return true; - } - - protected void renameOneIdentifier(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - mainFrame.getPanel().renameOneIdentifier(swf); - } - - protected void renameInvalidIdentifiers(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - mainFrame.getPanel().renameIdentifiers(swf); - } - - protected void deobfuscationActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - mainFrame.getPanel().deobfuscate(); - } - - protected void setSubLimiter(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - AbstractButton button = (AbstractButton) evt.getSource(); - boolean selected = button.isSelected(); - Main.setSubLimiter(selected); - } - - protected void switchDebugger() { - DebuggerTools.switchDebugger(swf); - } - - protected void debuggerShowLogActionPerformed(ActionEvent evt) { - DebuggerTools.debuggerShowLog(); - } - - protected void debuggerInjectLoader(ActionEvent evt) { - DebuggerTools.injectDebugLoader(swf); - refreshDecompiled(); - } - - protected void debuggerReplaceTraceCallsActionPerformed(ActionEvent evt) { - ReplaceTraceDialog rtd = new ReplaceTraceDialog(Configuration.lastDebuggerReplaceFunction.get()); - rtd.setVisible(true); - if (rtd.getValue() != null) { - String fname = rtd.getValue(); - DebuggerTools.replaceTraceCalls(swf, fname); - mainFrame.getPanel().refreshDecompiled(); - Configuration.lastDebuggerReplaceFunction.set(rtd.getValue()); - } - } - - protected void clearRecentFilesActionPerformed(ActionEvent evt) { - Configuration.recentFiles.set(null); - } - - protected void removeNonScripts() { - mainFrame.getPanel().removeNonScripts(swf); - } - - protected void removeExceptSelected() { - mainFrame.getPanel().removeExceptSelected(swf); - } - - protected void refreshDecompiled() { - mainFrame.getPanel().refreshDecompiled(); - } - - protected boolean previousTag(ActionEvent evt) { - return mainFrame.getPanel().previousTag(); - } - - protected boolean nextTag(ActionEvent evt) { - return mainFrame.getPanel().nextTag(); - } - - protected void checkResources() { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - PrintStream stream = new PrintStream(os); - CheckResources.checkResources(stream, null); - final String str = new String(os.toByteArray(), Utf8Helper.charset); - JDialog dialog = new JDialog() { - @Override - public void setVisible(boolean bln) { - setSize(new Dimension(800, 600)); - Container cnt = getContentPane(); - cnt.setLayout(new BorderLayout()); - String[] languages = SelectLanguageDialog.getAvailableLanguages().clone(); - languages[0] = "all"; - JComboBox languagesComboBox = new JComboBox<>(languages); - this.add(languagesComboBox, BorderLayout.NORTH); - ScrollPane scrollPane = new ScrollPane(); - JEditorPane editor = new JEditorPane(); - editor.setEditable(false); - editor.setText(str); - scrollPane.add(editor); - this.add(scrollPane, BorderLayout.CENTER); - this.setModal(true); - View.centerScreen(this); - languagesComboBox.addActionListener((ActionEvent e) -> { - String lang = (String) languagesComboBox.getSelectedItem(); - if (lang.equals("all")) { - lang = null; - } - ByteArrayOutputStream os = new ByteArrayOutputStream(); - try (PrintStream stream = new PrintStream(os, false, "UTF-8")) { - CheckResources.checkResources(stream, lang); - String str = new String(os.toByteArray(), Utf8Helper.charset); - editor.setText(str); - } catch (UnsupportedEncodingException ex) { - // ignore - } - }); - super.setVisible(bln); - } - }; - dialog.setVisible(true); - } - - protected void checkUpdatesActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - if (!Main.checkForUpdates()) { - View.showMessageDialog(null, translate("update.check.nonewversion"), translate("update.check.title"), JOptionPane.INFORMATION_MESSAGE); - } - } - - protected void helpUsActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - String helpUsURL = ApplicationInfo.PROJECT_PAGE + "/help_us.html?utm_source=app&utm_medium=menu&utm_campaign=app"; - if (!View.navigateUrl(helpUsURL)) { - View.showMessageDialog(null, translate("message.helpus").replace("%url%", helpUsURL)); - } - } - - protected void homePageActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - String homePageURL = ApplicationInfo.PROJECT_PAGE + "?utm_source=app&utm_medium=menu&utm_campaign=app"; - if (!View.navigateUrl(homePageURL)) { - View.showMessageDialog(null, translate("message.homepage").replace("%url%", homePageURL)); - } - } - - protected void aboutActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - Main.about(); - } - - protected boolean reloadActionPerformed(ActionEvent evt) { - if (swf != null) { - if (View.showConfirmDialog(null, translate("message.confirm.reload"), translate("message.warning"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { - Main.reloadFile(swf.swfList); - } - } - return true; - } - - protected boolean reloadAllActionPerformed(ActionEvent evt) { - if (swf != null) { - if (View.showConfirmDialog(null, translate("message.confirm.reloadAll"), translate("message.warning"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { - Main.reloadApp(); - } - - return true; - } - - Main.reloadApp(); - return true; - } - - protected void advancedSettingsActionPerformed(ActionEvent evt) { - Main.advancedSettings(); - } - - protected void searchMemoryActionPerformed(ActionEvent evt) { - Main.loadFromMemory(); - } - - protected void searchCacheActionPerformed(ActionEvent evt) { - Main.loadFromCache(); - } - - protected void gotoDucumentClassOnStartupActionPerformed(ActionEvent evt) { - AbstractButton button = (AbstractButton) evt.getSource(); - boolean selected = button.isSelected(); - - Configuration.gotoMainClassOnStartup.set(selected); - } - - protected void autoOpenLoadedSWFsActionPerformed(ActionEvent evt) { - AbstractButton button = (AbstractButton) evt.getSource(); - boolean selected = button.isSelected(); - - Configuration.autoOpenLoadedSWFs.set(selected); - } - - protected void autoRenameIdentifiersActionPerformed(ActionEvent evt) { - AbstractButton button = (AbstractButton) evt.getSource(); - boolean selected = button.isSelected(); - - Configuration.autoRenameIdentifiers.set(selected); - } - - /*protected void cacheOnDiskActionPerformed(ActionEvent evt) { - AbstractButton button = (AbstractButton) evt.getSource(); - boolean selected = button.isSelected(); - - Configuration.cacheOnDisk.set(selected); - if (selected) { - Cache.setStorageType(Cache.STORAGE_FILES); - } else { - Cache.setStorageType(Cache.STORAGE_MEMORY); - } - }*/ - protected void setLanguageActionPerformed(ActionEvent evt) { - new SelectLanguageDialog().display(); - } - - protected void disableDecompilationActionPerformed(ActionEvent evt) { - AbstractButton button = (AbstractButton) evt.getSource(); - boolean selected = button.isSelected(); - - Configuration.decompile.set(!selected); - mainFrame.getPanel().disableDecompilationChanged(); - } - - protected void associateActionPerformed(ActionEvent evt) { - AbstractButton button = (AbstractButton) evt.getSource(); - boolean selected = button.isSelected(); - - if (selected == ContextMenuTools.isAddedToContextMenu()) { - return; - } - ContextMenuTools.addToContextMenu(selected, false); - - // Update checkbox menuitem accordingly (User can cancel rights elevation) - new Timer().schedule(new TimerTask() { - @Override - public void run() { - button.setSelected(ContextMenuTools.isAddedToContextMenu()); - } - }, 1000); // It takes some time registry change to apply - } - - protected void gotoDucumentClassActionPerformed(ActionEvent evt) { - mainFrame.getPanel().gotoDocumentClass(mainFrame.getPanel().getCurrentSwf()); - } - - protected void parallelSpeedUpActionPerformed(ActionEvent evt) { - AbstractButton button = (AbstractButton) evt.getSource(); - boolean selected = button.isSelected(); - - String confStr = translate("message.confirm.parallel") + "\r\n"; - if (selected) { - confStr += " " + translate("message.confirm.on"); - } else { - confStr += " " + translate("message.confirm.off"); - } - if (View.showConfirmDialog(null, confStr, translate("message.parallel"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { - Configuration.parallelSpeedUp.set(selected); - } else { - button.setSelected(Configuration.parallelSpeedUp.get()); - } - } - - protected void internalViewerSwitchActionPerformed(ActionEvent evt) { - AbstractButton button = (AbstractButton) evt.getSource(); - boolean selected = button.isSelected(); - - Configuration.internalFlashViewer.set(selected); - mainFrame.getPanel().reload(true); - } - - protected void simplifyExpressionsActionPerformed(ActionEvent evt) { - AbstractButton button = (AbstractButton) evt.getSource(); - boolean selected = button.isSelected(); - - Configuration.simplifyExpressions.set(selected); - mainFrame.getPanel().autoDeobfuscateChanged(); - } - - protected void autoDeobfuscationActionPerformed(ActionEvent evt) { - AbstractButton button = (AbstractButton) evt.getSource(); - boolean selected = button.isSelected(); - - if (View.showConfirmDialog(mainFrame.getPanel(), translate("message.confirm.autodeobfuscate") + "\r\n" + (selected ? translate("message.confirm.on") : translate("message.confirm.off")), translate("message.confirm"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { - Configuration.autoDeobfuscate.set(selected); - mainFrame.getPanel().autoDeobfuscateChanged(); - } else { - button.setSelected(Configuration.autoDeobfuscate.get()); - } - } - - /*protected void deobfuscationMode(ActionEvent evt, int mode) { - Configuration.deobfuscationMode.set(mode); - mainFrame.getPanel().autoDeobfuscateChanged(); - }*/ - protected void exitActionPerformed(ActionEvent evt) { - JFrame frame = (JFrame) mainFrame; - frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); - } - - public void updateComponents() { - updateComponents(swf); - } - - public void updateComponents(SWF swf) { - this.swf = swf; - boolean isRunning = Main.isRunning(); - boolean isDebugRunning = Main.isDebugRunning(); - boolean isDebugPaused = Main.isDebugPaused(); - - boolean isRunningOrDebugging = isRunning || isDebugRunning; - - boolean swfSelected = swf != null; - boolean isWorking = Main.isWorking(); - List abcList = swf != null ? swf.getAbcList() : null; - boolean hasAbc = swfSelected && abcList != null && !abcList.isEmpty(); - boolean hasDebugger = hasAbc && DebuggerTools.hasDebugger(swf); - MainPanel mainPanel = mainFrame.getPanel(); - boolean swfLoaded = mainPanel != null ? !mainPanel.getSwfs().isEmpty() : false; - - setMenuEnabled("_/open", !isWorking); - setMenuEnabled("/file/open", !isWorking); - setMenuEnabled("_/save", swfSelected && !isWorking); - setMenuEnabled("/file/save", swfSelected && !isWorking); - setMenuEnabled("_/saveAs", swfSelected && !isWorking); - setMenuEnabled("/file/saveAs", swfSelected && !isWorking); - setMenuEnabled("/file/saveAsExe", swfSelected && !isWorking); - setMenuEnabled("_/close", swfSelected && !isWorking); - setMenuEnabled("/file/close", swfSelected && !isWorking); - setMenuEnabled("_/closeAll", swfLoaded && !isWorking); - setMenuEnabled("/file/closeAll", swfLoaded && !isWorking); - - setMenuEnabled("/file/export", swfSelected); - setMenuEnabled("_/exportAll", swfSelected && !isWorking); - setMenuEnabled("/file/export/exportAll", swfSelected && !isWorking); - setMenuEnabled("_/exportFla", swfSelected && !isWorking); - setMenuEnabled("/file/export/exportFla", swfSelected && !isWorking); - setMenuEnabled("_/exportSelected", swfSelected && !isWorking); - setMenuEnabled("/file/export/exportSelected", swfSelected && !isWorking); - setMenuEnabled("/file/export/exportXml", swfSelected && !isWorking); - - setMenuEnabled("/file/import", swfSelected); - setMenuEnabled("/file/import/importText", swfSelected && !isWorking); - setMenuEnabled("/file/import/importScript", swfSelected && !isWorking); - setMenuEnabled("/file/import/importSymbolClass", swfSelected && !isWorking); - setMenuEnabled("/file/import/importXml", swfSelected && !isWorking); - - setMenuEnabled("/tools/deobfuscation", swfSelected); - setMenuEnabled("/tools/deobfuscation/renameOneIdentifier", swfSelected && !isWorking); - setMenuEnabled("/tools/deobfuscation/renameInvalidIdentifiers", swfSelected && !isWorking); - setMenuEnabled("/tools/deobfuscation/deobfuscation", hasAbc); - - setMenuEnabled("/tools/search", swfSelected); - setMenuEnabled("/tools/replace", swfSelected); - setMenuEnabled("/tools/timeline", swfSelected); - setMenuEnabled("/tools/showProxy", !isWorking); - - setMenuEnabled("/tools/gotoDocumentClass", hasAbc); - /*setMenuEnabled("/tools/debugger/debuggerSwitch", hasAbc); - setMenuChecked("/tools/debugger/debuggerSwitch", hasDebugger); - setMenuEnabled("/tools/debugger/debuggerReplaceTrace", hasAbc && hasDebugger);*/ - //setMenuEnabled("/tools/debugger/debuggerInjectLoader", hasAbc && hasDebugger); - - setMenuEnabled("_/checkUpdates", !isWorking); - setMenuEnabled("/help/checkUpdates", !isWorking); - setMenuEnabled("/help/helpUs", !isWorking); - setMenuEnabled("/help/homePage", !isWorking); - setMenuEnabled("_/about", !isWorking); - setMenuEnabled("/help/about", !isWorking); - - setMenuEnabled("/file/start/run", swfSelected && !isRunningOrDebugging); - setMenuEnabled("/file/start/debug", swfSelected && !isRunningOrDebugging); - setMenuEnabled("/file/start/debugpcode", swfSelected && !isRunningOrDebugging); - - setMenuEnabled("/file/start/stop", isRunningOrDebugging); - setMenuEnabled("/debugging/debug/stop", isRunningOrDebugging); //same as previous - - setPathVisible("/debugging", isDebugRunning); - setMenuEnabled("/debugging/debug", isDebugRunning); - //setMenuEnabled("/debugging/debug/pause", isDebugRunning); - setMenuEnabled("/debugging/debug/stepOver", isDebugPaused); - setMenuEnabled("/debugging/debug/stepInto", isDebugPaused); - setMenuEnabled("/debugging/debug/stepOut", isDebugPaused); - setMenuEnabled("/debugging/debug/continue", isDebugPaused); - //setMenuEnabled("/debugging/debug/stack", isDebugPaused); - //setMenuEnabled("/debugging/debug/watch", isDebugPaused); - - } - - private void registerHotKeys() { - - KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - manager.addKeyEventDispatcher(keyEventDispatcher = this::dispatchKeyEvent); - } - - public void createMenuBar() { - initMenu(); - - if (supportsAppMenu()) { - addMenuItem("_", null, null, null, 0, null, false, null, false); - addMenuItem("_/open", translate("menu.file.open"), "open32", this::openActionPerformed, PRIORITY_TOP, this::loadRecent, false, null, false); - addMenuItem("_/save", translate("menu.file.save"), "save32", this::saveActionPerformed, PRIORITY_TOP, null, true, null, false); - addMenuItem("_/saveAs", translate("menu.file.saveas"), "saveas32", this::saveAsActionPerformed, PRIORITY_TOP, null, true, null, false); - addSeparator("_"); - addMenuItem("_/exportFla", translate("menu.file.export.fla"), "exportfla32", this::exportFlaActionPerformed, PRIORITY_TOP, null, true, null, false); - addMenuItem("_/exportAll", translate("menu.file.export.all"), "export32", this::exportAllActionPerformed, PRIORITY_TOP, null, true, null, false); - addMenuItem("_/exportSelected", translate("menu.file.export.selection"), "exportsel32", this::exportSelectedActionPerformed, PRIORITY_TOP, null, true, null, false); - addSeparator("_"); - addMenuItem("_/checkUpdates", translate("menu.help.checkupdates"), "update32", this::checkUpdatesActionPerformed, PRIORITY_TOP, null, true, null, false); - addMenuItem("_/about", translate("menu.help.about"), "about32", this::aboutActionPerformed, PRIORITY_TOP, null, true, null, false); - addMenuItem("_/close", translate("menu.file.close"), "close32", this::closeActionPerformed, PRIORITY_TOP, null, true, null, false); - addMenuItem("_/closeAll", translate("menu.file.closeAll"), "closeall32", this::closeAllActionPerformed, PRIORITY_TOP, null, true, null, false); - addMenuItem("_/$exit", translate("menu.file.exit"), "exit32", this::exitActionPerformed, PRIORITY_TOP, null, true, null, false); - finishMenu("_"); - } - - addMenuItem("/file", translate("menu.file"), null, null, 0, null, false, null, false); - addMenuItem("/file/open", translate("menu.file.open"), "open32", this::openActionPerformed, PRIORITY_TOP, this::loadRecent, !supportsMenuAction(), new HotKey("CTRL+SHIFT+O"), false); - - if (!supportsMenuAction()) { - addMenuItem("/file/recent", translate("menu.recentFiles"), null, null, 0, this::loadRecent, false, null, false); - finishMenu("/file/recent"); - } else { - finishMenu("/file/open"); - } - - addMenuItem("/file/save", translate("menu.file.save"), "save32", this::saveActionPerformed, PRIORITY_TOP, null, true, new HotKey("CTRL+SHIFT+S"), false); - addMenuItem("/file/saveAs", translate("menu.file.saveas"), "saveas16", this::saveAsActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+SHIFT+A"), false); - addMenuItem("/file/saveAsExe", translate("menu.file.saveasexe"), "saveasexe16", this::saveAsExeActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/file/reload", translate("menu.file.reload"), "reload16", this::reloadActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+SHIFT+R"), false); - addMenuItem("/file/reloadAll", translate("menu.file.reloadAll"), "reload16", this::reloadAllActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - - addSeparator("/file"); - - addMenuItem("/file/export", translate("menu.export"), null, null, 0, null, false, null, false); - addMenuItem("/file/export/exportFla", translate("menu.file.export.fla"), "exportfla32", this::exportFlaActionPerformed, PRIORITY_TOP, null, true, null, false); - addMenuItem("/file/export/exportXml", translate("menu.file.export.xml"), "exportxml32", this::exportXmlActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/file/export/exportAll", translate("menu.file.export.all"), "export16", this::exportAllActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+SHIFT+E"), false); - addMenuItem("/file/export/exportSelected", translate("menu.file.export.selection"), "exportsel16", this::exportSelectedActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - finishMenu("/file/export"); - - addMenuItem("/file/import", translate("menu.import"), null, null, 0, null, false, null, false); - addMenuItem("/file/import/importXml", translate("menu.file.import.xml"), "importxml32", this::importXmlActionPerformed, PRIORITY_TOP, null, true, null, false); - addMenuItem("/file/import/importText", translate("menu.file.import.text"), "importtext32", this::importTextActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/file/import/importScript", translate("menu.file.import.script"), "importscript32", this::importScriptActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/file/import/importSymbolClass", translate("menu.file.import.symbolClass"), "importsymbolclass32", this::importSymbolClassActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - finishMenu("/file/import"); - - addMenuItem("/file/start", translate("menu.file.start"), null, null, 0, null, false, null, false); - addMenuItem("/file/start/run", translate("menu.file.start.run"), "play32", this::runActionPerformed, PRIORITY_TOP, null, true, new HotKey("F6"), false); - addMenuItem("/file/start/debug", translate("menu.file.start.debug"), "debug32", this::debugActionPerformed, PRIORITY_TOP, null, true, new HotKey("CTRL+F5"), false); - addMenuItem("/file/start/stop", translate("menu.file.start.stop"), "stop32", this::stopActionPerformed, PRIORITY_TOP, null, true, null, false); - addMenuItem("/file/start/debugpcode", translate("menu.file.start.debugpcode"), "debug32", this::debugPCodeActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - finishMenu("/file/start"); - - addMenuItem("/file/view", translate("menu.view"), null, null, 0, null, false, null, false); - addToggleMenuItem("/file/view/viewResources", translate("menu.file.view.resources"), "view", "viewresources16", this::viewResourcesActionPerformed, PRIORITY_MEDIUM, null); - addToggleMenuItem("/file/view/viewHex", translate("menu.file.view.hex"), "view", "viewhex16", this::viewHexActionPerformed, PRIORITY_MEDIUM, null); - finishMenu("/file/view"); - - addSeparator("/file"); - addMenuItem("/file/close", translate("menu.file.close"), "close32", this::closeActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/file/closeAll", translate("menu.file.closeAll"), "closeall32", this::closeAllActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+SHIFT+X"), false); - - if (!supportsAppMenu()) { - addSeparator("/file"); - addMenuItem("/file/exit", translate("menu.file.exit"), "exit32", this::exitActionPerformed, PRIORITY_TOP, null, true, null, false); - } - - finishMenu("/file"); - - if (Configuration.dumpView.get()) { - setGroupSelection("view", "/file/view/viewHex"); - } else { - setGroupSelection("view", "/file/view/viewResources"); - } - - /* - menu.file.start = Start - menu.file.start.run = Run - menu.file.start.stop = Stop - menu.file.start.debug = Debug - menu.debugging = Debugging - menu.debugging.debug = Debug - menu.debugging.debug.stop = Stop - menu.debugging.debug.pause = Pause - menu.debugging.debug.stepOver = Step over - menu.debugging.debug.stepInto = Step into - menu.debugging.debug.stepOut = Step out - menu.debugging.debug.continue = Continue - menu.debugging.debug.stack = Stack... - menu.debugging.debug.watch = New watch... - */ - addMenuItem("/debugging", translate("menu.debugging"), null, null, 0, null, false, null, true); - addMenuItem("/debugging/debug", translate("menu.debugging.debug"), null, null, 0, null, false, null, false); - addMenuItem("/debugging/debug/stop", translate("menu.file.start.stop"), "stop32", this::stopActionPerformed, PRIORITY_TOP, null, true, null, false); - //addMenuItem("/debugging/debug/pause", translate("menu.debugging.debug.pause"), "pause32", this::pauseActionPerformed, PRIORITY_TOP, null, true,false); - addMenuItem("/debugging/debug/continue", translate("menu.debugging.debug.continue"), "continue32", this::continueActionPerformed, PRIORITY_TOP, null, true, new HotKey("F5"), false); - addMenuItem("/debugging/debug/stepOver", translate("menu.debugging.debug.stepOver"), "stepover32", this::stepOverActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("F8"), false); - addMenuItem("/debugging/debug/stepInto", translate("menu.debugging.debug.stepInto"), "stepinto32", this::stepIntoActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("F7"), false); - addMenuItem("/debugging/debug/stepOut", translate("menu.debugging.debug.stepOut"), "stepout32", this::stepOutActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+F7"), false); - //addMenuItem("/debugging/debug/stack", translate("menu.debugging.debug.stack"), "stack32", this::stackActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - //addMenuItem("/debugging/debug/watch", translate("menu.debugging.debug.watch"), "watch32", this::watchActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - finishMenu("/debugging/debug"); - finishMenu("/debugging"); - - addMenuItem("/tools", translate("menu.tools"), null, null, 0, null, false, null, false); - addMenuItem("/tools/search", translate("menu.tools.search"), "search16", this::searchActionPerformed, PRIORITY_TOP, null, true, null, false); - - addMenuItem("/tools/replace", translate("menu.tools.replace"), "replace32", this::replaceActionPerformed, PRIORITY_TOP, null, true, null, false); - addToggleMenuItem("/tools/timeline", translate("menu.tools.timeline"), null, "timeline32", this::timelineActionPerformed, PRIORITY_TOP, null); - - addMenuItem("/tools/showProxy", translate("menu.tools.proxy"), "proxy16", this::showProxyActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - if (Platform.isWindows()) { - addMenuItem("/tools/searchMemory", translate("menu.tools.searchMemory"), "loadmemory16", this::searchMemoryActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - } - - //addMenuItem("/tools/searchCache", translate("menu.tools.searchCache"), "loadcache16", this::searchCacheActionPerformed, PRIORITY_MEDIUM, null, true, null); - addMenuItem("/tools/deobfuscation", translate("menu.tools.deobfuscation"), "deobfuscate16", null, 0, null, false, null, false); - addMenuItem("/tools/deobfuscation/renameOneIdentifier", translate("menu.tools.deobfuscation.globalrename"), "rename16", this::renameOneIdentifier, PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/tools/deobfuscation/renameInvalidIdentifiers", translate("menu.tools.deobfuscation.renameinvalid"), "renameall16", this::renameInvalidIdentifiers, PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/tools/deobfuscation/deobfuscation", translate("menu.tools.deobfuscation.pcode"), "deobfuscate32", this::deobfuscationActionPerformed, PRIORITY_TOP, null, true, null, false); - finishMenu("/tools/deobfuscation"); - - /*addMenuItem("/tools/debugger", translate("menu.debugger"), null, null, 0, null, false, null,false); - addToggleMenuItem("/tools/debugger/debuggerSwitch", translate("menu.debugger.switch"), null, "debugger32", this::debuggerSwitchActionPerformed, PRIORITY_TOP, null,false); - addMenuItem("/tools/debugger/debuggerReplaceTrace", translate("menu.debugger.replacetrace"), "debuggerreplace16", this::debuggerReplaceTraceCallsActionPerformed, PRIORITY_MEDIUM, null, true, null,false); - //addMenuItem("/tools/debugger/debuggerInjectLoader", "Inject Loader", "debuggerreplace16", this::debuggerInjectLoader, PRIORITY_MEDIUM, null, true,false); - addMenuItem("/tools/debugger/debuggerShowLog", translate("menu.debugger.showlog"), "debuggerlog16", this::debuggerShowLogActionPerformed, PRIORITY_MEDIUM, null, true, null,false); - finishMenu("/tools/debugger");*/ - addMenuItem("/tools/gotoDocumentClass", translate("menu.tools.gotoDocumentClass"), "gotomainclass32", this::gotoDucumentClassActionPerformed, PRIORITY_TOP, null, true, null, false); - finishMenu("/tools"); - - //Settings - addMenuItem("/settings", translate("menu.settings"), null, null, 0, null, false, null, false); - - addToggleMenuItem("/settings/autoDeobfuscation", translate("menu.settings.autodeobfuscation"), null, null, this::autoDeobfuscationActionPerformed, 0, null); - addToggleMenuItem("/settings/simplifyExpressions", translate("menu.settings.simplifyExpressions"), null, null, this::simplifyExpressionsActionPerformed, 0, null); - addToggleMenuItem("/settings/internalViewer", translate("menu.settings.internalflashviewer"), null, null, this::internalViewerSwitchActionPerformed, 0, null); - addToggleMenuItem("/settings/parallelSpeedUp", translate("menu.settings.parallelspeedup"), null, null, this::parallelSpeedUpActionPerformed, 0, null); - addToggleMenuItem("/settings/disableDecompilation", translate("menu.settings.disabledecompilation"), null, null, this::disableDecompilationActionPerformed, 0, null); - //addToggleMenuItem("/settings/cacheOnDisk", translate("menu.settings.cacheOnDisk"), null, null, this::cacheOnDiskActionPerformed, 0, null); - addToggleMenuItem("/settings/gotoMainClassOnStartup", translate("menu.settings.gotoMainClassOnStartup"), null, null, this::gotoDucumentClassOnStartupActionPerformed, 0, null); - addToggleMenuItem("/settings/autoRenameIdentifiers", translate("menu.settings.autoRenameIdentifiers"), null, null, this::autoRenameIdentifiersActionPerformed, 0, null); - addToggleMenuItem("/settings/autoOpenLoadedSWFs", translate("menu.settings.autoOpenLoadedSWFs"), null, null, this::autoOpenLoadedSWFsActionPerformed, 0, null); - if (Platform.isWindows()) { - addToggleMenuItem("/settings/associate", translate("menu.settings.addtocontextmenu"), null, null, this::associateActionPerformed, 0, null); - } - - addMenuItem("/settings/language", translate("menu.language"), null, null, 0, null, false, null, false); - addMenuItem("/settings/language/setLanguage", translate("menu.settings.language"), "setlanguage32", this::setLanguageActionPerformed, PRIORITY_TOP, null, true, null, false); - finishMenu("/settings/language"); - - /*addMenuItem("/settings/deobfuscation", translate("menu.deobfuscation"), null, null, 0, null, false,false); - addToggleMenuItem("/settings/deobfuscation/old", translate("menu.file.deobfuscation.old"), "deobfuscation", "deobfuscateold16", (ActionEvent e) -> { - deobfuscationMode(e, 0); - }, 0); - addToggleMenuItem("/settings/deobfuscation/new", translate("menu.file.deobfuscation.new"), "deobfuscation", "deobfuscatenew16", (ActionEvent e) -> { - deobfuscationMode(e, 1); - }, 0); - - finishMenu("/settings/deobfuscation");*/ - addMenuItem("/settings/advancedSettings", translate("menu.advancedsettings.advancedsettings"), null, null, 0, null, false, null, false); - addMenuItem("/settings/advancedSettings/advancedSettings", translate("menu.advancedsettings.advancedsettings"), "settings32", this::advancedSettingsActionPerformed, PRIORITY_TOP, null, true, null, false); - addMenuItem("/settings/advancedSettings/clearRecentFiles", translate("menu.tools.otherTools.clearRecentFiles"), "clearrecent16", this::clearRecentFilesActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - finishMenu("/settings/advancedSettings"); - - finishMenu("/settings"); - - setMenuChecked("/settings/autoDeobfuscation", Configuration.autoDeobfuscate.get()); - Configuration.autoDeobfuscate.addListener(configListenerAutoDeobfuscate = (Boolean newValue) -> { - setMenuChecked("/settings/autoDeobfuscation", newValue); - }); - - setMenuChecked("/settings/simplifyExpressions", Configuration.simplifyExpressions.get()); - Configuration.simplifyExpressions.addListener(configListenerSimplifyExpressions = (Boolean newValue) -> { - setMenuChecked("/settings/simplifyExpressions", newValue); - }); - - setMenuChecked("/settings/internalViewer", Configuration.internalFlashViewer.get() || externalFlashPlayerUnavailable); - Configuration.internalFlashViewer.addListener(configListenerInternalFlashViewer = (Boolean newValue) -> { - setMenuChecked("/settings/internalViewer", newValue || externalFlashPlayerUnavailable); - }); - - setMenuChecked("/settings/parallelSpeedUp", Configuration.parallelSpeedUp.get()); - Configuration.parallelSpeedUp.addListener(configListenerParallelSpeedUp = (Boolean newValue) -> { - setMenuChecked("/settings/parallelSpeedUp", newValue); - }); - - setMenuChecked("/settings/disableDecompilation", !Configuration.decompile.get()); - Configuration.decompile.addListener(configListenerDecompile = (Boolean newValue) -> { - setMenuChecked("/settings/disableDecompilation", !newValue); - }); - - /*setMenuChecked("/settings/cacheOnDisk", Configuration.cacheOnDisk.get()); - Configuration.cacheOnDisk.addListener(configListenerCacheOnDisk = (Boolean newValue) -> { - setMenuChecked("/settings/cacheOnDisk", newValue); - });*/ - setMenuChecked("/settings/gotoMainClassOnStartup", Configuration.gotoMainClassOnStartup.get()); - Configuration.gotoMainClassOnStartup.addListener(configListenerGotoMainClassOnStartup = (Boolean newValue) -> { - setMenuChecked("/settings/gotoMainClassOnStartup", newValue); - }); - - setMenuChecked("/settings/autoRenameIdentifiers", Configuration.autoRenameIdentifiers.get()); - Configuration.autoRenameIdentifiers.addListener(configListenerAutoRenameIdentifiers = (Boolean newValue) -> { - setMenuChecked("/settings/autoRenameIdentifiers", newValue); - }); - - setMenuChecked("/settings/autoOpenLoadedSWFs", Configuration.autoOpenLoadedSWFs.get()); - Configuration.autoOpenLoadedSWFs.addListener(configListenerAutoOpenLoadedSWFs = (Boolean newValue) -> { - setMenuChecked("/settings/autoOpenLoadedSWFs", newValue); - }); - - if (externalFlashPlayerUnavailable) { - setMenuEnabled("/settings/internalViewer", false); - setMenuEnabled("/settings/autoOpenLoadedSWFs", false); - } - - /*int deobfuscationMode = Configuration.deobfuscationMode.get(); - switch (deobfuscationMode) { - case 0: - setGroupSelection("deobfuscation", "/settings/deobfuscation/old"); - break; - case 1: - setGroupSelection("deobfuscation", "/settings/deobfuscation/new"); - break; - }*/ - if (Platform.isWindows()) { - setMenuChecked("/settings/associate", ContextMenuTools.isAddedToContextMenu()); - } - - //Help - addMenuItem("/help", translate("menu.help"), null, null, 0, null, false, null, false); - addMenuItem("/help/helpUs", translate("menu.help.helpus"), "donate32", this::helpUsActionPerformed, PRIORITY_TOP, null, true, null, false); - addMenuItem("/help/homePage", translate("menu.help.homepage"), "homepage16", this::homePageActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - addSeparator("/help"); - addMenuItem("/help/checkUpdates", translate("menu.help.checkupdates"), "update16", this::checkUpdatesActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/help/about", translate("menu.help.about"), "about32", this::aboutActionPerformed, PRIORITY_TOP, null, true, null, false); - finishMenu("/help"); - - if (Configuration._showDebugMenu.get() || Configuration._debugMode.get()) { - - addMenuItem("/debug", "# FFDec Debug #", null, null, 0, null, false, null, false); - addMenuItem("/debug/removeNonScripts", "Remove non scripts", "continue16", e -> removeNonScripts(), PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/debug/removeExceptSelected", "Remove except selected", "continue16", e -> removeExceptSelected(), PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/debug/refreshDecompiled", "Refresh decompiled script", "continue16", e -> refreshDecompiled(), PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/debug/checkResources", "Check resources", "continue16", e -> checkResources(), PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/debug/callGc", "Call System.gc()", "continue16", e -> System.gc(), PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/debug/emptyCache", "Empty cache", "continue16", e -> { - SWF nswf = mainFrame.getPanel().getCurrentSwf(); - if (nswf != null) { - nswf.clearAllCache(); - } - }, PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/debug/memoryInformation", "Memory information", "continue16", e -> { - String architecture = System.getProperty("sun.arch.data.model"); - Runtime runtime = Runtime.getRuntime(); - String info = "Architecture: " + architecture + Helper.newLine - + "Jre 64bit: " + Helper.is64BitJre() + Helper.newLine - + "Os 64bit: " + Helper.is64BitOs() + Helper.newLine - + "Max: " + (runtime.maxMemory() / 1024 / 1024) + "MB" + Helper.newLine - + "Used: " + (runtime.totalMemory() / 1024 / 1024) + "MB" + Helper.newLine - + "Free: " + (runtime.freeMemory() / 1024 / 1024) + "MB"; - View.showMessageDialog(null, info); - SWF nswf = mainFrame.getPanel().getCurrentSwf(); - if (nswf != null) { - nswf.clearAllCache(); - } - }, PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/debug/fixAs3Code", "Fix AS3 code", "continue16", e -> { - SWF nswf = mainFrame.getPanel().getCurrentSwf(); - if (nswf != null) { - nswf.fixAS3Code(); - } - }, PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/debug/openTestSwfs", "Open test SWFs", "continue16", e -> { - String path; - - SWFSourceInfo[] sourceInfos = new SWFSourceInfo[2]; - String mainPath = Main.class.getProtectionDomain().getCodeSource().getLocation().getPath(); - path = mainPath + "\\..\\..\\libsrc\\ffdec_lib\\testdata\\as2\\as2.swf"; - sourceInfos[0] = new SWFSourceInfo(null, path, null); - path = mainPath + "\\..\\..\\libsrc\\ffdec_lib\\testdata\\as3\\as3.swf"; - sourceInfos[1] = new SWFSourceInfo(null, path, null); - Main.openFile(sourceInfos); - }, PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/debug/createNewSwf", "Create new SWF", "continue16", e -> { - SWF swf = new SWF(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - swf.saveTo(baos); - } catch (IOException ex) { - Logger.getLogger(MainFrameMenu.class.getName()).log(Level.SEVERE, null, ex); - } - - Main.openFile(new SWFSourceInfo(new ByteArrayInputStream(baos.toByteArray()), "New SWF", "New SWF")); - }, PRIORITY_MEDIUM, null, true, null, false); - finishMenu("/debug"); - } - - finishMenu(""); - } - - public void showResourcesView() { - viewResourcesActionPerformed(null); - } - - private void viewResourcesActionPerformed(ActionEvent evt) { - Configuration.dumpView.set(false); - mainFrame.getPanel().showView(MainPanel.VIEW_RESOURCES); - setGroupSelection("view", "/file/view/viewResources"); - setMenuChecked("/tools/timeline", false); - } - - private void viewHexActionPerformed(ActionEvent evt) { - Configuration.dumpView.set(true); - MainPanel mainPanel = mainFrame.getPanel(); - if (mainPanel.isModified()) { - View.showMessageDialog(null, translate("message.warning.hexViewNotUpToDate"), translate("message.warning"), JOptionPane.WARNING_MESSAGE, Configuration.warningHexViewNotUpToDate); - } - - mainPanel.showView(MainPanel.VIEW_DUMP); - setGroupSelection("view", "/file/view/viewHex"); - setMenuChecked("/tools/timeline", false); - } - - private void debuggerSwitchActionPerformed(ActionEvent evt) { - boolean debuggerOn = isMenuChecked("/tools/debugger/debuggerSwitch"); - if (!debuggerOn || View.showConfirmDialog((Component) mainFrame, translate("message.debugger"), translate("dialog.message.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE, Configuration.displayDebuggerInfo, JOptionPane.OK_OPTION) == JOptionPane.OK_OPTION) { - switchDebugger(); - mainFrame.getPanel().refreshDecompiled(); - } else { - setMenuChecked("/tools/debugger/debuggerSwitch", false); - } - setMenuEnabled("/tools/debugger/debuggerReplaceTrace", isMenuChecked("/tools/debugger/debuggerSwitch")); - //setMenuEnabled("/tools/debugger/debuggerInjectLoader", isMenuChecked("/tools/debugger/debuggerSwitch")); - } - - private void timelineActionPerformed(ActionEvent evt) { - if (isMenuChecked("/tools/timeline")) { - if (!mainFrame.getPanel().showView(MainPanel.VIEW_TIMELINE)) { - setMenuChecked("/tools/timeline", false); - } else { - setGroupSelection("view", null); - } - } else if (Configuration.dumpView.get()) { - setGroupSelection("view", "/file/view/viewHex"); - mainFrame.getPanel().showView(MainPanel.VIEW_DUMP); - } else { - setGroupSelection("view", "/file/view/viewResources"); - mainFrame.getPanel().showView(MainPanel.VIEW_RESOURCES); - } - } - - protected void loadRecent(ActionEvent evt) { - List recentFiles = Configuration.getRecentFiles(); - clearMenu("/file/" + (supportsMenuAction() ? "open" : "recent")); - clearMenu("_/open"); - - for (int i = recentFiles.size() - 1; i >= 0; i--) { - final String f = recentFiles.get(i); - ActionListener a = (ActionEvent e) -> { - if (Main.openFile(f, null) == OpenFileResult.NOT_FOUND) { - if (View.showConfirmDialog(null, translate("message.confirm.recentFileNotFound"), translate("message.confirm"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_NO_OPTION) { - Configuration.removeRecentFile(f); - } - } - }; - addMenuItem("/file/" + (supportsMenuAction() ? "open" : "recent") + "/" + i, f, null, a, 0, null, true, null, false); - addMenuItem("_/open/" + i, f, null, a, 0, null, true, null, false); - } - - finishMenu("/file/" + (supportsMenuAction() ? "open" : "recent")); - finishMenu("_/open"); - } - - public void dispose() { - KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - manager.removeKeyEventDispatcher(keyEventDispatcher); - - Configuration.autoDeobfuscate.removeListener(configListenerAutoDeobfuscate); - Configuration.simplifyExpressions.removeListener(configListenerSimplifyExpressions); - Configuration.internalFlashViewer.removeListener(configListenerInternalFlashViewer); - Configuration.parallelSpeedUp.removeListener(configListenerParallelSpeedUp); - Configuration.decompile.removeListener(configListenerDecompile); - //Configuration.cacheOnDisk.removeListener(configListenerCacheOnDisk); - Configuration.gotoMainClassOnStartup.removeListener(configListenerGotoMainClassOnStartup); - Configuration.autoRenameIdentifiers.removeListener(configListenerAutoRenameIdentifiers); - Configuration.autoOpenLoadedSWFs.removeListener(configListenerAutoOpenLoadedSWFs); - - Main.stopRun(); - } - - public boolean runActionPerformed(ActionEvent evt) { - Main.run(swf); - return true; - } - - public boolean debugActionPerformed(ActionEvent evt) { - Main.runDebug(swf, false); - return true; - } - - public boolean debugPCodeActionPerformed(ActionEvent evt) { - Main.runDebug(swf, true); - return true; - } - - public boolean stopActionPerformed(ActionEvent evt) { - Main.stopRun(); - return true; - } - - public boolean pauseActionPerformed(ActionEvent evt) { - try { - DebuggerCommands cmd = Main.getDebugHandler().getCommands(); - //TODO - - } catch (IOException ex) { - Main.getDebugHandler().disconnect(); - //ignore - } - return true; - } - - public boolean stepOverActionPerformed(ActionEvent evt) { - - try { - - DebuggerCommands cmd = Main.getDebugHandler().getCommands(); - mainFrame.getPanel().clearDebuggerColors(); - Main.startWork(AppStrings.translate("work.debugging") + "...", null); - - cmd.stepOver(); - } catch (IOException ex) { - Main.getDebugHandler().disconnect(); - //ignore - } - return true; - } - - public boolean stepIntoActionPerformed(ActionEvent evt) { - try { - DebuggerCommands cmd = Main.getDebugHandler().getCommands(); - mainFrame.getPanel().clearDebuggerColors(); - Main.startWork(AppStrings.translate("work.debugging") + "...", null); - - cmd.stepInto(); - } catch (IOException ex) { - Main.getDebugHandler().disconnect(); - //ignore - } - - return true; - } - - public boolean stepOutActionPerformed(ActionEvent evt) { - try { - DebuggerCommands cmd = Main.getDebugHandler().getCommands(); - mainFrame.getPanel().clearDebuggerColors(); - Main.startWork(AppStrings.translate("work.debugging") + "...", null); - cmd.stepOut(); - } catch (IOException ex) { - Main.getDebugHandler().disconnect(); - //ignore - } - - return true; - } - - public boolean continueActionPerformed(ActionEvent evt) { - try { - DebuggerCommands cmd = Main.getDebugHandler().getCommands(); - mainFrame.getPanel().clearDebuggerColors(); - Main.startWork(AppStrings.translate("work.debugging") + "...", null); - cmd.sendContinue(); - } catch (IOException ex) { - Main.getDebugHandler().disconnect(); - //ignore - } - - return true; - } - - public boolean stackActionPerformed(ActionEvent evt) { - //TODO - return true; - } - - public boolean watchActionPerformed(ActionEvent evt) { - //TODO - return true; - } - - public boolean dispatchKeyEvent(KeyEvent e) { - if (((JFrame) mainFrame).isActive() && e.getID() == KeyEvent.KEY_PRESSED) { - - HotKey ek = new HotKey(e); - for (String path : menuHotkeys.keySet()) { - HotKey mk = menuHotkeys.get(path); - if (ek.equals(mk)) { - if (menuActions.containsKey(path)) { - menuActions.get(path).actionPerformed(null); - return true; - } - } - } - - //other nonmenu actions - int code = e.getKeyCode(); - if (e.isControlDown() && e.isShiftDown()) { //CTRL+SHIFT - switch (code) { - case KeyEvent.VK_F: - return searchInActionPerformed(null); - case KeyEvent.VK_T: - return searchInTextPerformed(null); - case KeyEvent.VK_D: - return clearLog(null); - } - } else if (e.isControlDown() && !e.isShiftDown()) { //CTRL - switch (code) { - case KeyEvent.VK_UP: - return previousTag(null); - case KeyEvent.VK_DOWN: - return nextTag(null); - } - } - } - - return false; - } - - public abstract void hilightPath(String path); - - public abstract void setPathVisible(String path, boolean val); -} +/* + * Copyright (C) 2010-2016 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.debugger.flash.DebuggerCommands; +import com.jpexs.decompiler.flash.ApplicationInfo; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFBundle; +import com.jpexs.decompiler.flash.SWFSourceInfo; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.configuration.ConfigurationItemChangeListener; +import com.jpexs.decompiler.flash.console.ContextMenuTools; +import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools; +import com.jpexs.decompiler.flash.gui.helpers.CheckResources; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.utf8.Utf8Helper; +import com.sun.jna.Platform; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.KeyEventDispatcher; +import java.awt.KeyboardFocusManager; +import java.awt.ScrollPane; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.AbstractButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JOptionPane; + +/** + * + * @author JPEXS + */ +public abstract class MainFrameMenu implements MenuBuilder { + + private final MainFrame mainFrame; + + private KeyEventDispatcher keyEventDispatcher; + + private SWF swf; + + private ConfigurationItemChangeListener configListenerAutoDeobfuscate; + + private ConfigurationItemChangeListener configListenerSimplifyExpressions; + + private ConfigurationItemChangeListener configListenerInternalFlashViewer; + + private ConfigurationItemChangeListener configListenerParallelSpeedUp; + + private ConfigurationItemChangeListener configListenerDecompile; + + //private ConfigurationItemChangeListener configListenerCacheOnDisk; + private ConfigurationItemChangeListener configListenerGotoMainClassOnStartup; + + private ConfigurationItemChangeListener configListenerAutoRenameIdentifiers; + + private ConfigurationItemChangeListener configListenerAutoOpenLoadedSWFs; + + protected final Map menuHotkeys = new HashMap<>(); + + @Override + public HotKey getMenuHotkey(String path) { + return menuHotkeys.get(path); + } + + protected final Map menuActions = new HashMap<>(); + + public boolean isInternalFlashViewerSelected() { + return isMenuChecked("/settings/internalViewer"); //miInternalViewer.isSelected(); + } + + private final boolean externalFlashPlayerUnavailable; + + public MainFrameMenu(MainFrame mainFrame, boolean externalFlashPlayerUnavailable) { + registerHotKeys(); + this.mainFrame = mainFrame; + this.externalFlashPlayerUnavailable = externalFlashPlayerUnavailable; + } + + protected String translate(String key) { + return mainFrame.translate(key); + } + + protected boolean openActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return false; + } + + Main.openFileDialog(); + return true; + } + + protected boolean saveActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return false; + } + + if (swf != null) { + boolean saved = false; + if (swf.swfList != null && swf.swfList.isBundle()) { + SWFBundle bundle = swf.swfList.bundle; + if (!bundle.isReadOnly()) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + swf.saveTo(baos); + saved = bundle.putSWF(swf.getFileTitle(), new ByteArrayInputStream(baos.toByteArray())); + } catch (IOException ex) { + Logger.getLogger(MainFrameMenu.class.getName()).log(Level.SEVERE, "Cannot save SWF", ex); + } + } + } else if (swf.binaryData != null) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + swf.saveTo(baos); + swf.binaryData.binaryData = new ByteArrayRange(baos.toByteArray()); + swf.binaryData.setModified(true); + saved = true; + } catch (IOException ex) { + Logger.getLogger(MainFrameMenu.class.getName()).log(Level.SEVERE, "Cannot save SWF", ex); + } + } else if (swf.getFile() == null) { + saved = saveAs(swf, SaveFileMode.SAVEAS); + } else { + try { + Main.saveFile(swf, swf.getFile()); + saved = true; + } catch (IOException ex) { + Logger.getLogger(MainFrameMenu.class.getName()).log(Level.SEVERE, null, ex); + View.showMessageDialog(null, translate("error.file.save"), translate("error"), JOptionPane.ERROR_MESSAGE); + } + } + if (saved) { + swf.clearModified(); + mainFrame.getPanel().refreshTree(swf); + } + + return true; + } + + return false; + } + + protected boolean saveAsActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return false; + } + + if (swf != null) { + if (saveAs(swf, SaveFileMode.SAVEAS)) { + swf.clearModified(); + } + + return true; + } + + return false; + } + + private boolean saveAs(SWF swf, SaveFileMode mode) { + if (Main.saveFileDialog(swf, mode)) { + mainFrame.setTitle(ApplicationInfo.applicationVerName + (Configuration.displayFileName.get() ? " - " + swf.getFileTitle() : "")); + updateComponents(swf); + return true; + } + return false; + } + + protected void saveAsExeActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + if (swf != null) { + saveAs(swf, SaveFileMode.EXE); + } + } + + protected void closeActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + if (swf == null) { + return; + } + + Main.closeFile(swf.swfList); + } + + protected boolean closeAllActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return false; + } + + if (swf != null) { + return Main.closeAll(); + } + + return false; + } + + protected void importTextActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + mainFrame.getPanel().importText(swf); + } + + protected void importScriptActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + mainFrame.getPanel().importScript(swf); + } + + protected void importSymbolClassActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + mainFrame.getPanel().importSymbolClass(swf); + } + + protected boolean exportAllActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return false; + } + + return export(false); + } + + protected boolean exportSelectedActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return false; + } + + return export(true); + } + + protected boolean export(boolean onlySelected) { + if (swf != null) { + mainFrame.getPanel().export(onlySelected); + return true; + } + + return false; + } + + protected void exportFlaActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + mainFrame.getPanel().exportFla(swf); + } + + protected void importXmlActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + mainFrame.getPanel().importSwfXml(); + } + + protected void exportXmlActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + mainFrame.getPanel().exportSwfXml(); + } + + protected boolean searchActionPerformed(ActionEvent evt) { + return search(evt, null); + } + + protected boolean searchInTextPerformed(ActionEvent evt) { + return search(evt, true); + } + + protected boolean searchInActionPerformed(ActionEvent evt) { + return search(evt, false); + } + + protected boolean search(ActionEvent evt, Boolean searchInText) { + if (swf != null) { + mainFrame.getPanel().searchInActionScriptOrText(searchInText, swf); + return true; + } + + return false; + } + + protected boolean replaceActionPerformed(ActionEvent evt) { + if (swf != null) { + mainFrame.getPanel().replaceText(); + return true; + } + + return false; + } + + protected void showProxyActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + Main.showProxy(); + } + + protected boolean clearLog(ActionEvent evt) { + ErrorLogFrame.getInstance().clearLog(); + return true; + } + + protected void renameOneIdentifier(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + mainFrame.getPanel().renameOneIdentifier(swf); + } + + protected void renameInvalidIdentifiers(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + mainFrame.getPanel().renameIdentifiers(swf); + } + + protected void deobfuscationActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + mainFrame.getPanel().deobfuscate(); + } + + protected void setSubLimiter(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + AbstractButton button = (AbstractButton) evt.getSource(); + boolean selected = button.isSelected(); + Main.setSubLimiter(selected); + } + + protected void switchDebugger() { + DebuggerTools.switchDebugger(swf); + } + + protected void debuggerShowLogActionPerformed(ActionEvent evt) { + DebuggerTools.debuggerShowLog(); + } + + protected void debuggerInjectLoader(ActionEvent evt) { + DebuggerTools.injectDebugLoader(swf); + refreshDecompiled(); + } + + protected void debuggerReplaceTraceCallsActionPerformed(ActionEvent evt) { + ReplaceTraceDialog rtd = new ReplaceTraceDialog(Configuration.lastDebuggerReplaceFunction.get()); + rtd.setVisible(true); + if (rtd.getValue() != null) { + String fname = rtd.getValue(); + DebuggerTools.replaceTraceCalls(swf, fname); + mainFrame.getPanel().refreshDecompiled(); + Configuration.lastDebuggerReplaceFunction.set(rtd.getValue()); + } + } + + protected void clearRecentFilesActionPerformed(ActionEvent evt) { + Configuration.recentFiles.set(null); + } + + protected void removeNonScripts() { + mainFrame.getPanel().removeNonScripts(swf); + } + + protected void removeExceptSelected() { + mainFrame.getPanel().removeExceptSelected(swf); + } + + protected void refreshDecompiled() { + mainFrame.getPanel().refreshDecompiled(); + } + + protected boolean previousTag(ActionEvent evt) { + return mainFrame.getPanel().previousTag(); + } + + protected boolean nextTag(ActionEvent evt) { + return mainFrame.getPanel().nextTag(); + } + + protected void checkResources() { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + PrintStream stream = new PrintStream(os); + CheckResources.checkResources(stream, null); + final String str = new String(os.toByteArray(), Utf8Helper.charset); + JDialog dialog = new JDialog() { + @Override + public void setVisible(boolean bln) { + setSize(new Dimension(800, 600)); + Container cnt = getContentPane(); + cnt.setLayout(new BorderLayout()); + String[] languages = SelectLanguageDialog.getAvailableLanguages().clone(); + languages[0] = "all"; + JComboBox languagesComboBox = new JComboBox<>(languages); + this.add(languagesComboBox, BorderLayout.NORTH); + ScrollPane scrollPane = new ScrollPane(); + JEditorPane editor = new JEditorPane(); + editor.setEditable(false); + editor.setText(str); + scrollPane.add(editor); + this.add(scrollPane, BorderLayout.CENTER); + this.setModal(true); + View.centerScreen(this); + languagesComboBox.addActionListener((ActionEvent e) -> { + String lang = (String) languagesComboBox.getSelectedItem(); + if (lang.equals("all")) { + lang = null; + } + ByteArrayOutputStream os = new ByteArrayOutputStream(); + try (PrintStream stream = new PrintStream(os, false, "UTF-8")) { + CheckResources.checkResources(stream, lang); + String str = new String(os.toByteArray(), Utf8Helper.charset); + editor.setText(str); + } catch (UnsupportedEncodingException ex) { + // ignore + } + }); + super.setVisible(bln); + } + }; + dialog.setVisible(true); + } + + protected void checkUpdatesActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + if (!Main.checkForUpdates()) { + View.showMessageDialog(null, translate("update.check.nonewversion"), translate("update.check.title"), JOptionPane.INFORMATION_MESSAGE); + } + } + + protected void helpUsActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + String helpUsURL = ApplicationInfo.PROJECT_PAGE + "/help_us.html?utm_source=app&utm_medium=menu&utm_campaign=app"; + if (!View.navigateUrl(helpUsURL)) { + View.showMessageDialog(null, translate("message.helpus").replace("%url%", helpUsURL)); + } + } + + protected void homePageActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + String homePageURL = ApplicationInfo.PROJECT_PAGE + "?utm_source=app&utm_medium=menu&utm_campaign=app"; + if (!View.navigateUrl(homePageURL)) { + View.showMessageDialog(null, translate("message.homepage").replace("%url%", homePageURL)); + } + } + + protected void aboutActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + Main.about(); + } + + protected boolean reloadActionPerformed(ActionEvent evt) { + if (swf != null) { + if (View.showConfirmDialog(null, translate("message.confirm.reload"), translate("message.warning"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { + Main.reloadFile(swf.swfList); + } + } + return true; + } + + protected boolean reloadAllActionPerformed(ActionEvent evt) { + if (swf != null) { + if (View.showConfirmDialog(null, translate("message.confirm.reloadAll"), translate("message.warning"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { + Main.reloadApp(); + } + + return true; + } + + Main.reloadApp(); + return true; + } + + protected void advancedSettingsActionPerformed(ActionEvent evt) { + Main.advancedSettings(); + } + + protected void searchMemoryActionPerformed(ActionEvent evt) { + Main.loadFromMemory(); + } + + protected void searchCacheActionPerformed(ActionEvent evt) { + Main.loadFromCache(); + } + + protected void gotoDucumentClassOnStartupActionPerformed(ActionEvent evt) { + AbstractButton button = (AbstractButton) evt.getSource(); + boolean selected = button.isSelected(); + + Configuration.gotoMainClassOnStartup.set(selected); + } + + protected void autoOpenLoadedSWFsActionPerformed(ActionEvent evt) { + AbstractButton button = (AbstractButton) evt.getSource(); + boolean selected = button.isSelected(); + + Configuration.autoOpenLoadedSWFs.set(selected); + } + + protected void autoRenameIdentifiersActionPerformed(ActionEvent evt) { + AbstractButton button = (AbstractButton) evt.getSource(); + boolean selected = button.isSelected(); + + Configuration.autoRenameIdentifiers.set(selected); + } + + /*protected void cacheOnDiskActionPerformed(ActionEvent evt) { + AbstractButton button = (AbstractButton) evt.getSource(); + boolean selected = button.isSelected(); + + Configuration.cacheOnDisk.set(selected); + if (selected) { + Cache.setStorageType(Cache.STORAGE_FILES); + } else { + Cache.setStorageType(Cache.STORAGE_MEMORY); + } + }*/ + protected void setLanguageActionPerformed(ActionEvent evt) { + new SelectLanguageDialog().display(); + } + + protected void disableDecompilationActionPerformed(ActionEvent evt) { + AbstractButton button = (AbstractButton) evt.getSource(); + boolean selected = button.isSelected(); + + Configuration.decompile.set(!selected); + mainFrame.getPanel().disableDecompilationChanged(); + } + + protected void associateActionPerformed(ActionEvent evt) { + AbstractButton button = (AbstractButton) evt.getSource(); + boolean selected = button.isSelected(); + + if (selected == ContextMenuTools.isAddedToContextMenu()) { + return; + } + ContextMenuTools.addToContextMenu(selected, false); + + // Update checkbox menuitem accordingly (User can cancel rights elevation) + new Timer().schedule(new TimerTask() { + @Override + public void run() { + button.setSelected(ContextMenuTools.isAddedToContextMenu()); + } + }, 1000); // It takes some time registry change to apply + } + + protected void gotoDucumentClassActionPerformed(ActionEvent evt) { + mainFrame.getPanel().gotoDocumentClass(mainFrame.getPanel().getCurrentSwf()); + } + + protected void parallelSpeedUpActionPerformed(ActionEvent evt) { + AbstractButton button = (AbstractButton) evt.getSource(); + boolean selected = button.isSelected(); + + String confStr = translate("message.confirm.parallel") + "\r\n"; + if (selected) { + confStr += " " + translate("message.confirm.on"); + } else { + confStr += " " + translate("message.confirm.off"); + } + if (View.showConfirmDialog(null, confStr, translate("message.parallel"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { + Configuration.parallelSpeedUp.set(selected); + } else { + button.setSelected(Configuration.parallelSpeedUp.get()); + } + } + + protected void internalViewerSwitchActionPerformed(ActionEvent evt) { + AbstractButton button = (AbstractButton) evt.getSource(); + boolean selected = button.isSelected(); + + Configuration.internalFlashViewer.set(selected); + mainFrame.getPanel().reload(true); + } + + protected void simplifyExpressionsActionPerformed(ActionEvent evt) { + AbstractButton button = (AbstractButton) evt.getSource(); + boolean selected = button.isSelected(); + + Configuration.simplifyExpressions.set(selected); + mainFrame.getPanel().autoDeobfuscateChanged(); + } + + protected void autoDeobfuscationActionPerformed(ActionEvent evt) { + AbstractButton button = (AbstractButton) evt.getSource(); + boolean selected = button.isSelected(); + + if (View.showConfirmDialog(mainFrame.getPanel(), translate("message.confirm.autodeobfuscate") + "\r\n" + (selected ? translate("message.confirm.on") : translate("message.confirm.off")), translate("message.confirm"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { + Configuration.autoDeobfuscate.set(selected); + mainFrame.getPanel().autoDeobfuscateChanged(); + } else { + button.setSelected(Configuration.autoDeobfuscate.get()); + } + } + + /*protected void deobfuscationMode(ActionEvent evt, int mode) { + Configuration.deobfuscationMode.set(mode); + mainFrame.getPanel().autoDeobfuscateChanged(); + }*/ + protected void exitActionPerformed(ActionEvent evt) { + JFrame frame = (JFrame) mainFrame; + frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); + } + + public void updateComponents() { + updateComponents(swf); + } + + public void updateComponents(SWF swf) { + this.swf = swf; + boolean isRunning = Main.isRunning(); + boolean isDebugRunning = Main.isDebugRunning(); + boolean isDebugPaused = Main.isDebugPaused(); + + boolean isRunningOrDebugging = isRunning || isDebugRunning; + + boolean swfSelected = swf != null; + boolean isWorking = Main.isWorking(); + List abcList = swf != null ? swf.getAbcList() : null; + boolean hasAbc = swfSelected && abcList != null && !abcList.isEmpty(); + boolean hasDebugger = hasAbc && DebuggerTools.hasDebugger(swf); + MainPanel mainPanel = mainFrame.getPanel(); + boolean swfLoaded = mainPanel != null ? !mainPanel.getSwfs().isEmpty() : false; + + setMenuEnabled("_/open", !isWorking); + setMenuEnabled("/file/open", !isWorking); + setMenuEnabled("_/save", swfSelected && !isWorking); + setMenuEnabled("/file/save", swfSelected && !isWorking); + setMenuEnabled("_/saveAs", swfSelected && !isWorking); + setMenuEnabled("/file/saveAs", swfSelected && !isWorking); + setMenuEnabled("/file/saveAsExe", swfSelected && !isWorking); + setMenuEnabled("_/close", swfSelected && !isWorking); + setMenuEnabled("/file/close", swfSelected && !isWorking); + setMenuEnabled("_/closeAll", swfLoaded && !isWorking); + setMenuEnabled("/file/closeAll", swfLoaded && !isWorking); + + setMenuEnabled("/file/export", swfSelected); + setMenuEnabled("_/exportAll", swfSelected && !isWorking); + setMenuEnabled("/file/export/exportAll", swfSelected && !isWorking); + setMenuEnabled("_/exportFla", swfSelected && !isWorking); + setMenuEnabled("/file/export/exportFla", swfSelected && !isWorking); + setMenuEnabled("_/exportSelected", swfSelected && !isWorking); + setMenuEnabled("/file/export/exportSelected", swfSelected && !isWorking); + setMenuEnabled("/file/export/exportXml", swfSelected && !isWorking); + + setMenuEnabled("/file/import", swfSelected); + setMenuEnabled("/file/import/importText", swfSelected && !isWorking); + setMenuEnabled("/file/import/importScript", swfSelected && !isWorking); + setMenuEnabled("/file/import/importSymbolClass", swfSelected && !isWorking); + setMenuEnabled("/file/import/importXml", swfSelected && !isWorking); + + setMenuEnabled("/tools/deobfuscation", swfSelected); + setMenuEnabled("/tools/deobfuscation/renameOneIdentifier", swfSelected && !isWorking); + setMenuEnabled("/tools/deobfuscation/renameInvalidIdentifiers", swfSelected && !isWorking); + setMenuEnabled("/tools/deobfuscation/deobfuscation", hasAbc); + + setMenuEnabled("/tools/search", swfSelected); + setMenuEnabled("/tools/replace", swfSelected); + setMenuEnabled("/tools/timeline", swfSelected); + setMenuEnabled("/tools/showProxy", !isWorking); + + setMenuEnabled("/tools/gotoDocumentClass", hasAbc); + /*setMenuEnabled("/tools/debugger/debuggerSwitch", hasAbc); + setMenuChecked("/tools/debugger/debuggerSwitch", hasDebugger); + setMenuEnabled("/tools/debugger/debuggerReplaceTrace", hasAbc && hasDebugger);*/ + //setMenuEnabled("/tools/debugger/debuggerInjectLoader", hasAbc && hasDebugger); + + setMenuEnabled("_/checkUpdates", !isWorking); + setMenuEnabled("/help/checkUpdates", !isWorking); + setMenuEnabled("/help/helpUs", !isWorking); + setMenuEnabled("/help/homePage", !isWorking); + setMenuEnabled("_/about", !isWorking); + setMenuEnabled("/help/about", !isWorking); + + setMenuEnabled("/file/start/run", swfSelected && !isRunningOrDebugging); + setMenuEnabled("/file/start/debug", swfSelected && !isRunningOrDebugging); + setMenuEnabled("/file/start/debugpcode", swfSelected && !isRunningOrDebugging); + + setMenuEnabled("/file/start/stop", isRunningOrDebugging); + setMenuEnabled("/debugging/debug/stop", isRunningOrDebugging); //same as previous + + setPathVisible("/debugging", isDebugRunning); + setMenuEnabled("/debugging/debug", isDebugRunning); + //setMenuEnabled("/debugging/debug/pause", isDebugRunning); + setMenuEnabled("/debugging/debug/stepOver", isDebugPaused); + setMenuEnabled("/debugging/debug/stepInto", isDebugPaused); + setMenuEnabled("/debugging/debug/stepOut", isDebugPaused); + setMenuEnabled("/debugging/debug/continue", isDebugPaused); + //setMenuEnabled("/debugging/debug/stack", isDebugPaused); + //setMenuEnabled("/debugging/debug/watch", isDebugPaused); + + } + + private void registerHotKeys() { + + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + manager.addKeyEventDispatcher(keyEventDispatcher = this::dispatchKeyEvent); + } + + public void createMenuBar() { + initMenu(); + + if (supportsAppMenu()) { + addMenuItem("_", null, null, null, 0, null, false, null, false); + addMenuItem("_/open", translate("menu.file.open"), "open32", this::openActionPerformed, PRIORITY_TOP, this::loadRecent, false, null, false); + addMenuItem("_/save", translate("menu.file.save"), "save32", this::saveActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("_/saveAs", translate("menu.file.saveas"), "saveas32", this::saveAsActionPerformed, PRIORITY_TOP, null, true, null, false); + addSeparator("_"); + addMenuItem("_/exportFla", translate("menu.file.export.fla"), "exportfla32", this::exportFlaActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("_/exportAll", translate("menu.file.export.all"), "export32", this::exportAllActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("_/exportSelected", translate("menu.file.export.selection"), "exportsel32", this::exportSelectedActionPerformed, PRIORITY_TOP, null, true, null, false); + addSeparator("_"); + addMenuItem("_/checkUpdates", translate("menu.help.checkupdates"), "update32", this::checkUpdatesActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("_/about", translate("menu.help.about"), "about32", this::aboutActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("_/close", translate("menu.file.close"), "close32", this::closeActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("_/closeAll", translate("menu.file.closeAll"), "closeall32", this::closeAllActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("_/$exit", translate("menu.file.exit"), "exit32", this::exitActionPerformed, PRIORITY_TOP, null, true, null, false); + finishMenu("_"); + } + + addMenuItem("/file", translate("menu.file"), null, null, 0, null, false, null, false); + addMenuItem("/file/open", translate("menu.file.open"), "open32", this::openActionPerformed, PRIORITY_TOP, this::loadRecent, !supportsMenuAction(), new HotKey("CTRL+SHIFT+O"), false); + + if (!supportsMenuAction()) { + addMenuItem("/file/recent", translate("menu.recentFiles"), null, null, 0, this::loadRecent, false, null, false); + finishMenu("/file/recent"); + } else { + finishMenu("/file/open"); + } + + addMenuItem("/file/save", translate("menu.file.save"), "save32", this::saveActionPerformed, PRIORITY_TOP, null, true, new HotKey("CTRL+SHIFT+S"), false); + addMenuItem("/file/saveAs", translate("menu.file.saveas"), "saveas16", this::saveAsActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+SHIFT+A"), false); + addMenuItem("/file/saveAsExe", translate("menu.file.saveasexe"), "saveasexe16", this::saveAsExeActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/file/reload", translate("menu.file.reload"), "reload16", this::reloadActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+SHIFT+R"), false); + addMenuItem("/file/reloadAll", translate("menu.file.reloadAll"), "reload16", this::reloadAllActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + + addSeparator("/file"); + + addMenuItem("/file/export", translate("menu.export"), null, null, 0, null, false, null, false); + addMenuItem("/file/export/exportFla", translate("menu.file.export.fla"), "exportfla32", this::exportFlaActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("/file/export/exportXml", translate("menu.file.export.xml"), "exportxml32", this::exportXmlActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/file/export/exportAll", translate("menu.file.export.all"), "export16", this::exportAllActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+SHIFT+E"), false); + addMenuItem("/file/export/exportSelected", translate("menu.file.export.selection"), "exportsel16", this::exportSelectedActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + finishMenu("/file/export"); + + addMenuItem("/file/import", translate("menu.import"), null, null, 0, null, false, null, false); + addMenuItem("/file/import/importXml", translate("menu.file.import.xml"), "importxml32", this::importXmlActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("/file/import/importText", translate("menu.file.import.text"), "importtext32", this::importTextActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/file/import/importScript", translate("menu.file.import.script"), "importscript32", this::importScriptActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/file/import/importSymbolClass", translate("menu.file.import.symbolClass"), "importsymbolclass32", this::importSymbolClassActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + finishMenu("/file/import"); + + addMenuItem("/file/start", translate("menu.file.start"), null, null, 0, null, false, null, false); + addMenuItem("/file/start/run", translate("menu.file.start.run"), "play32", this::runActionPerformed, PRIORITY_TOP, null, true, new HotKey("F6"), false); + addMenuItem("/file/start/debug", translate("menu.file.start.debug"), "debug32", this::debugActionPerformed, PRIORITY_TOP, null, true, new HotKey("CTRL+F5"), false); + addMenuItem("/file/start/stop", translate("menu.file.start.stop"), "stop32", this::stopActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("/file/start/debugpcode", translate("menu.file.start.debugpcode"), "debug32", this::debugPCodeActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + finishMenu("/file/start"); + + addMenuItem("/file/view", translate("menu.view"), null, null, 0, null, false, null, false); + addToggleMenuItem("/file/view/viewResources", translate("menu.file.view.resources"), "view", "viewresources16", this::viewResourcesActionPerformed, PRIORITY_MEDIUM, null); + addToggleMenuItem("/file/view/viewHex", translate("menu.file.view.hex"), "view", "viewhex16", this::viewHexActionPerformed, PRIORITY_MEDIUM, null); + finishMenu("/file/view"); + + addSeparator("/file"); + addMenuItem("/file/close", translate("menu.file.close"), "close32", this::closeActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/file/closeAll", translate("menu.file.closeAll"), "closeall32", this::closeAllActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+SHIFT+X"), false); + + if (!supportsAppMenu()) { + addSeparator("/file"); + addMenuItem("/file/exit", translate("menu.file.exit"), "exit32", this::exitActionPerformed, PRIORITY_TOP, null, true, null, false); + } + + finishMenu("/file"); + + if (Configuration.dumpView.get()) { + setGroupSelection("view", "/file/view/viewHex"); + } else { + setGroupSelection("view", "/file/view/viewResources"); + } + + /* + menu.file.start = Start + menu.file.start.run = Run + menu.file.start.stop = Stop + menu.file.start.debug = Debug + menu.debugging = Debugging + menu.debugging.debug = Debug + menu.debugging.debug.stop = Stop + menu.debugging.debug.pause = Pause + menu.debugging.debug.stepOver = Step over + menu.debugging.debug.stepInto = Step into + menu.debugging.debug.stepOut = Step out + menu.debugging.debug.continue = Continue + menu.debugging.debug.stack = Stack... + menu.debugging.debug.watch = New watch... + */ + addMenuItem("/debugging", translate("menu.debugging"), null, null, 0, null, false, null, true); + addMenuItem("/debugging/debug", translate("menu.debugging.debug"), null, null, 0, null, false, null, false); + addMenuItem("/debugging/debug/stop", translate("menu.file.start.stop"), "stop32", this::stopActionPerformed, PRIORITY_TOP, null, true, null, false); + //addMenuItem("/debugging/debug/pause", translate("menu.debugging.debug.pause"), "pause32", this::pauseActionPerformed, PRIORITY_TOP, null, true,false); + addMenuItem("/debugging/debug/continue", translate("menu.debugging.debug.continue"), "continue32", this::continueActionPerformed, PRIORITY_TOP, null, true, new HotKey("F5"), false); + addMenuItem("/debugging/debug/stepOver", translate("menu.debugging.debug.stepOver"), "stepover32", this::stepOverActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("F8"), false); + addMenuItem("/debugging/debug/stepInto", translate("menu.debugging.debug.stepInto"), "stepinto32", this::stepIntoActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("F7"), false); + addMenuItem("/debugging/debug/stepOut", translate("menu.debugging.debug.stepOut"), "stepout32", this::stepOutActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+F7"), false); + //addMenuItem("/debugging/debug/stack", translate("menu.debugging.debug.stack"), "stack32", this::stackActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + //addMenuItem("/debugging/debug/watch", translate("menu.debugging.debug.watch"), "watch32", this::watchActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + finishMenu("/debugging/debug"); + finishMenu("/debugging"); + + addMenuItem("/tools", translate("menu.tools"), null, null, 0, null, false, null, false); + addMenuItem("/tools/search", translate("menu.tools.search"), "search16", this::searchActionPerformed, PRIORITY_TOP, null, true, null, false); + + addMenuItem("/tools/replace", translate("menu.tools.replace"), "replace32", this::replaceActionPerformed, PRIORITY_TOP, null, true, null, false); + addToggleMenuItem("/tools/timeline", translate("menu.tools.timeline"), null, "timeline32", this::timelineActionPerformed, PRIORITY_TOP, null); + + addMenuItem("/tools/showProxy", translate("menu.tools.proxy"), "proxy16", this::showProxyActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + if (Platform.isWindows()) { + addMenuItem("/tools/searchMemory", translate("menu.tools.searchMemory"), "loadmemory16", this::searchMemoryActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + } + + //addMenuItem("/tools/searchCache", translate("menu.tools.searchCache"), "loadcache16", this::searchCacheActionPerformed, PRIORITY_MEDIUM, null, true, null); + addMenuItem("/tools/deobfuscation", translate("menu.tools.deobfuscation"), "deobfuscate16", null, 0, null, false, null, false); + addMenuItem("/tools/deobfuscation/renameOneIdentifier", translate("menu.tools.deobfuscation.globalrename"), "rename16", this::renameOneIdentifier, PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/tools/deobfuscation/renameInvalidIdentifiers", translate("menu.tools.deobfuscation.renameinvalid"), "renameall16", this::renameInvalidIdentifiers, PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/tools/deobfuscation/deobfuscation", translate("menu.tools.deobfuscation.pcode"), "deobfuscate32", this::deobfuscationActionPerformed, PRIORITY_TOP, null, true, null, false); + finishMenu("/tools/deobfuscation"); + + /*addMenuItem("/tools/debugger", translate("menu.debugger"), null, null, 0, null, false, null,false); + addToggleMenuItem("/tools/debugger/debuggerSwitch", translate("menu.debugger.switch"), null, "debugger32", this::debuggerSwitchActionPerformed, PRIORITY_TOP, null,false); + addMenuItem("/tools/debugger/debuggerReplaceTrace", translate("menu.debugger.replacetrace"), "debuggerreplace16", this::debuggerReplaceTraceCallsActionPerformed, PRIORITY_MEDIUM, null, true, null,false); + //addMenuItem("/tools/debugger/debuggerInjectLoader", "Inject Loader", "debuggerreplace16", this::debuggerInjectLoader, PRIORITY_MEDIUM, null, true,false); + addMenuItem("/tools/debugger/debuggerShowLog", translate("menu.debugger.showlog"), "debuggerlog16", this::debuggerShowLogActionPerformed, PRIORITY_MEDIUM, null, true, null,false); + finishMenu("/tools/debugger");*/ + addMenuItem("/tools/gotoDocumentClass", translate("menu.tools.gotoDocumentClass"), "gotomainclass32", this::gotoDucumentClassActionPerformed, PRIORITY_TOP, null, true, null, false); + finishMenu("/tools"); + + //Settings + addMenuItem("/settings", translate("menu.settings"), null, null, 0, null, false, null, false); + + addToggleMenuItem("/settings/autoDeobfuscation", translate("menu.settings.autodeobfuscation"), null, null, this::autoDeobfuscationActionPerformed, 0, null); + addToggleMenuItem("/settings/simplifyExpressions", translate("menu.settings.simplifyExpressions"), null, null, this::simplifyExpressionsActionPerformed, 0, null); + addToggleMenuItem("/settings/internalViewer", translate("menu.settings.internalflashviewer"), null, null, this::internalViewerSwitchActionPerformed, 0, null); + addToggleMenuItem("/settings/parallelSpeedUp", translate("menu.settings.parallelspeedup"), null, null, this::parallelSpeedUpActionPerformed, 0, null); + addToggleMenuItem("/settings/disableDecompilation", translate("menu.settings.disabledecompilation"), null, null, this::disableDecompilationActionPerformed, 0, null); + //addToggleMenuItem("/settings/cacheOnDisk", translate("menu.settings.cacheOnDisk"), null, null, this::cacheOnDiskActionPerformed, 0, null); + addToggleMenuItem("/settings/gotoMainClassOnStartup", translate("menu.settings.gotoMainClassOnStartup"), null, null, this::gotoDucumentClassOnStartupActionPerformed, 0, null); + addToggleMenuItem("/settings/autoRenameIdentifiers", translate("menu.settings.autoRenameIdentifiers"), null, null, this::autoRenameIdentifiersActionPerformed, 0, null); + addToggleMenuItem("/settings/autoOpenLoadedSWFs", translate("menu.settings.autoOpenLoadedSWFs"), null, null, this::autoOpenLoadedSWFsActionPerformed, 0, null); + if (Platform.isWindows()) { + addToggleMenuItem("/settings/associate", translate("menu.settings.addtocontextmenu"), null, null, this::associateActionPerformed, 0, null); + } + + addMenuItem("/settings/language", translate("menu.language"), null, null, 0, null, false, null, false); + addMenuItem("/settings/language/setLanguage", translate("menu.settings.language"), "setlanguage32", this::setLanguageActionPerformed, PRIORITY_TOP, null, true, null, false); + finishMenu("/settings/language"); + + /*addMenuItem("/settings/deobfuscation", translate("menu.deobfuscation"), null, null, 0, null, false,false); + addToggleMenuItem("/settings/deobfuscation/old", translate("menu.file.deobfuscation.old"), "deobfuscation", "deobfuscateold16", (ActionEvent e) -> { + deobfuscationMode(e, 0); + }, 0); + addToggleMenuItem("/settings/deobfuscation/new", translate("menu.file.deobfuscation.new"), "deobfuscation", "deobfuscatenew16", (ActionEvent e) -> { + deobfuscationMode(e, 1); + }, 0); + + finishMenu("/settings/deobfuscation");*/ + addMenuItem("/settings/advancedSettings", translate("menu.advancedsettings.advancedsettings"), null, null, 0, null, false, null, false); + addMenuItem("/settings/advancedSettings/advancedSettings", translate("menu.advancedsettings.advancedsettings"), "settings32", this::advancedSettingsActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("/settings/advancedSettings/clearRecentFiles", translate("menu.tools.otherTools.clearRecentFiles"), "clearrecent16", this::clearRecentFilesActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + finishMenu("/settings/advancedSettings"); + + finishMenu("/settings"); + + setMenuChecked("/settings/autoDeobfuscation", Configuration.autoDeobfuscate.get()); + Configuration.autoDeobfuscate.addListener(configListenerAutoDeobfuscate = (Boolean newValue) -> { + setMenuChecked("/settings/autoDeobfuscation", newValue); + }); + + setMenuChecked("/settings/simplifyExpressions", Configuration.simplifyExpressions.get()); + Configuration.simplifyExpressions.addListener(configListenerSimplifyExpressions = (Boolean newValue) -> { + setMenuChecked("/settings/simplifyExpressions", newValue); + }); + + setMenuChecked("/settings/internalViewer", Configuration.internalFlashViewer.get() || externalFlashPlayerUnavailable); + Configuration.internalFlashViewer.addListener(configListenerInternalFlashViewer = (Boolean newValue) -> { + setMenuChecked("/settings/internalViewer", newValue || externalFlashPlayerUnavailable); + }); + + setMenuChecked("/settings/parallelSpeedUp", Configuration.parallelSpeedUp.get()); + Configuration.parallelSpeedUp.addListener(configListenerParallelSpeedUp = (Boolean newValue) -> { + setMenuChecked("/settings/parallelSpeedUp", newValue); + }); + + setMenuChecked("/settings/disableDecompilation", !Configuration.decompile.get()); + Configuration.decompile.addListener(configListenerDecompile = (Boolean newValue) -> { + setMenuChecked("/settings/disableDecompilation", !newValue); + }); + + /*setMenuChecked("/settings/cacheOnDisk", Configuration.cacheOnDisk.get()); + Configuration.cacheOnDisk.addListener(configListenerCacheOnDisk = (Boolean newValue) -> { + setMenuChecked("/settings/cacheOnDisk", newValue); + });*/ + setMenuChecked("/settings/gotoMainClassOnStartup", Configuration.gotoMainClassOnStartup.get()); + Configuration.gotoMainClassOnStartup.addListener(configListenerGotoMainClassOnStartup = (Boolean newValue) -> { + setMenuChecked("/settings/gotoMainClassOnStartup", newValue); + }); + + setMenuChecked("/settings/autoRenameIdentifiers", Configuration.autoRenameIdentifiers.get()); + Configuration.autoRenameIdentifiers.addListener(configListenerAutoRenameIdentifiers = (Boolean newValue) -> { + setMenuChecked("/settings/autoRenameIdentifiers", newValue); + }); + + setMenuChecked("/settings/autoOpenLoadedSWFs", Configuration.autoOpenLoadedSWFs.get()); + Configuration.autoOpenLoadedSWFs.addListener(configListenerAutoOpenLoadedSWFs = (Boolean newValue) -> { + setMenuChecked("/settings/autoOpenLoadedSWFs", newValue); + }); + + if (externalFlashPlayerUnavailable) { + setMenuEnabled("/settings/internalViewer", false); + setMenuEnabled("/settings/autoOpenLoadedSWFs", false); + } + + /*int deobfuscationMode = Configuration.deobfuscationMode.get(); + switch (deobfuscationMode) { + case 0: + setGroupSelection("deobfuscation", "/settings/deobfuscation/old"); + break; + case 1: + setGroupSelection("deobfuscation", "/settings/deobfuscation/new"); + break; + }*/ + if (Platform.isWindows()) { + setMenuChecked("/settings/associate", ContextMenuTools.isAddedToContextMenu()); + } + + //Help + addMenuItem("/help", translate("menu.help"), null, null, 0, null, false, null, false); + addMenuItem("/help/helpUs", translate("menu.help.helpus"), "donate32", this::helpUsActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("/help/homePage", translate("menu.help.homepage"), "homepage16", this::homePageActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + addSeparator("/help"); + addMenuItem("/help/checkUpdates", translate("menu.help.checkupdates"), "update16", this::checkUpdatesActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/help/about", translate("menu.help.about"), "about32", this::aboutActionPerformed, PRIORITY_TOP, null, true, null, false); + finishMenu("/help"); + + if (Configuration._showDebugMenu.get() || Configuration._debugMode.get()) { + + addMenuItem("/debug", "# FFDec Debug #", null, null, 0, null, false, null, false); + addMenuItem("/debug/removeNonScripts", "Remove non scripts", "continue16", e -> removeNonScripts(), PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/debug/removeExceptSelected", "Remove except selected", "continue16", e -> removeExceptSelected(), PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/debug/refreshDecompiled", "Refresh decompiled script", "continue16", e -> refreshDecompiled(), PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/debug/checkResources", "Check resources", "continue16", e -> checkResources(), PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/debug/callGc", "Call System.gc()", "continue16", e -> System.gc(), PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/debug/emptyCache", "Empty cache", "continue16", e -> { + SWF nswf = mainFrame.getPanel().getCurrentSwf(); + if (nswf != null) { + nswf.clearAllCache(); + } + }, PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/debug/memoryInformation", "Memory information", "continue16", e -> { + String architecture = System.getProperty("sun.arch.data.model"); + Runtime runtime = Runtime.getRuntime(); + String info = "Architecture: " + architecture + Helper.newLine + + "Jre 64bit: " + Helper.is64BitJre() + Helper.newLine + + "Os 64bit: " + Helper.is64BitOs() + Helper.newLine + + "Max: " + (runtime.maxMemory() / 1024 / 1024) + "MB" + Helper.newLine + + "Used: " + (runtime.totalMemory() / 1024 / 1024) + "MB" + Helper.newLine + + "Free: " + (runtime.freeMemory() / 1024 / 1024) + "MB"; + View.showMessageDialog(null, info); + SWF nswf = mainFrame.getPanel().getCurrentSwf(); + if (nswf != null) { + nswf.clearAllCache(); + } + }, PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/debug/fixAs3Code", "Fix AS3 code", "continue16", e -> { + SWF nswf = mainFrame.getPanel().getCurrentSwf(); + if (nswf != null) { + nswf.fixAS3Code(); + } + }, PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/debug/openTestSwfs", "Open test SWFs", "continue16", e -> { + String path; + + SWFSourceInfo[] sourceInfos = new SWFSourceInfo[2]; + String mainPath = Main.class.getProtectionDomain().getCodeSource().getLocation().getPath(); + path = mainPath + "\\..\\..\\libsrc\\ffdec_lib\\testdata\\as2\\as2.swf"; + sourceInfos[0] = new SWFSourceInfo(null, path, null); + path = mainPath + "\\..\\..\\libsrc\\ffdec_lib\\testdata\\as3\\as3.swf"; + sourceInfos[1] = new SWFSourceInfo(null, path, null); + Main.openFile(sourceInfos); + }, PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/debug/createNewSwf", "Create new SWF", "continue16", e -> { + SWF swf = new SWF(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + swf.saveTo(baos); + } catch (IOException ex) { + Logger.getLogger(MainFrameMenu.class.getName()).log(Level.SEVERE, null, ex); + } + + Main.openFile(new SWFSourceInfo(new ByteArrayInputStream(baos.toByteArray()), "New SWF", "New SWF")); + }, PRIORITY_MEDIUM, null, true, null, false); + finishMenu("/debug"); + } + + finishMenu(""); + } + + public void showResourcesView() { + viewResourcesActionPerformed(null); + } + + private void viewResourcesActionPerformed(ActionEvent evt) { + Configuration.dumpView.set(false); + mainFrame.getPanel().showView(MainPanel.VIEW_RESOURCES); + setGroupSelection("view", "/file/view/viewResources"); + setMenuChecked("/tools/timeline", false); + } + + private void viewHexActionPerformed(ActionEvent evt) { + Configuration.dumpView.set(true); + MainPanel mainPanel = mainFrame.getPanel(); + if (mainPanel.isModified()) { + View.showMessageDialog(null, translate("message.warning.hexViewNotUpToDate"), translate("message.warning"), JOptionPane.WARNING_MESSAGE, Configuration.warningHexViewNotUpToDate); + } + + mainPanel.showView(MainPanel.VIEW_DUMP); + setGroupSelection("view", "/file/view/viewHex"); + setMenuChecked("/tools/timeline", false); + } + + private void debuggerSwitchActionPerformed(ActionEvent evt) { + boolean debuggerOn = isMenuChecked("/tools/debugger/debuggerSwitch"); + if (!debuggerOn || View.showConfirmDialog((Component) mainFrame, translate("message.debugger"), translate("dialog.message.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE, Configuration.displayDebuggerInfo, JOptionPane.OK_OPTION) == JOptionPane.OK_OPTION) { + switchDebugger(); + mainFrame.getPanel().refreshDecompiled(); + } else { + setMenuChecked("/tools/debugger/debuggerSwitch", false); + } + setMenuEnabled("/tools/debugger/debuggerReplaceTrace", isMenuChecked("/tools/debugger/debuggerSwitch")); + //setMenuEnabled("/tools/debugger/debuggerInjectLoader", isMenuChecked("/tools/debugger/debuggerSwitch")); + } + + private void timelineActionPerformed(ActionEvent evt) { + if (isMenuChecked("/tools/timeline")) { + if (!mainFrame.getPanel().showView(MainPanel.VIEW_TIMELINE)) { + setMenuChecked("/tools/timeline", false); + } else { + setGroupSelection("view", null); + } + } else if (Configuration.dumpView.get()) { + setGroupSelection("view", "/file/view/viewHex"); + mainFrame.getPanel().showView(MainPanel.VIEW_DUMP); + } else { + setGroupSelection("view", "/file/view/viewResources"); + mainFrame.getPanel().showView(MainPanel.VIEW_RESOURCES); + } + } + + protected void loadRecent(ActionEvent evt) { + List recentFiles = Configuration.getRecentFiles(); + clearMenu("/file/" + (supportsMenuAction() ? "open" : "recent")); + clearMenu("_/open"); + + for (int i = recentFiles.size() - 1; i >= 0; i--) { + final String f = recentFiles.get(i); + ActionListener a = (ActionEvent e) -> { + if (Main.openFile(f, null) == OpenFileResult.NOT_FOUND) { + if (View.showConfirmDialog(null, translate("message.confirm.recentFileNotFound"), translate("message.confirm"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_NO_OPTION) { + Configuration.removeRecentFile(f); + } + } + }; + addMenuItem("/file/" + (supportsMenuAction() ? "open" : "recent") + "/" + i, f, null, a, 0, null, true, null, false); + addMenuItem("_/open/" + i, f, null, a, 0, null, true, null, false); + } + + finishMenu("/file/" + (supportsMenuAction() ? "open" : "recent")); + finishMenu("_/open"); + } + + public void dispose() { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + manager.removeKeyEventDispatcher(keyEventDispatcher); + + Configuration.autoDeobfuscate.removeListener(configListenerAutoDeobfuscate); + Configuration.simplifyExpressions.removeListener(configListenerSimplifyExpressions); + Configuration.internalFlashViewer.removeListener(configListenerInternalFlashViewer); + Configuration.parallelSpeedUp.removeListener(configListenerParallelSpeedUp); + Configuration.decompile.removeListener(configListenerDecompile); + //Configuration.cacheOnDisk.removeListener(configListenerCacheOnDisk); + Configuration.gotoMainClassOnStartup.removeListener(configListenerGotoMainClassOnStartup); + Configuration.autoRenameIdentifiers.removeListener(configListenerAutoRenameIdentifiers); + Configuration.autoOpenLoadedSWFs.removeListener(configListenerAutoOpenLoadedSWFs); + + Main.stopRun(); + } + + public boolean runActionPerformed(ActionEvent evt) { + Main.run(swf); + return true; + } + + public boolean debugActionPerformed(ActionEvent evt) { + Main.runDebug(swf, false); + return true; + } + + public boolean debugPCodeActionPerformed(ActionEvent evt) { + Main.runDebug(swf, true); + return true; + } + + public boolean stopActionPerformed(ActionEvent evt) { + Main.stopRun(); + return true; + } + + public boolean pauseActionPerformed(ActionEvent evt) { + try { + DebuggerCommands cmd = Main.getDebugHandler().getCommands(); + //TODO + + } catch (IOException ex) { + Main.getDebugHandler().disconnect(); + //ignore + } + return true; + } + + public boolean stepOverActionPerformed(ActionEvent evt) { + + try { + + DebuggerCommands cmd = Main.getDebugHandler().getCommands(); + mainFrame.getPanel().clearDebuggerColors(); + Main.startWork(AppStrings.translate("work.debugging") + "...", null); + + cmd.stepOver(); + } catch (IOException ex) { + Main.getDebugHandler().disconnect(); + //ignore + } + return true; + } + + public boolean stepIntoActionPerformed(ActionEvent evt) { + try { + DebuggerCommands cmd = Main.getDebugHandler().getCommands(); + mainFrame.getPanel().clearDebuggerColors(); + Main.startWork(AppStrings.translate("work.debugging") + "...", null); + + cmd.stepInto(); + } catch (IOException ex) { + Main.getDebugHandler().disconnect(); + //ignore + } + + return true; + } + + public boolean stepOutActionPerformed(ActionEvent evt) { + try { + DebuggerCommands cmd = Main.getDebugHandler().getCommands(); + mainFrame.getPanel().clearDebuggerColors(); + Main.startWork(AppStrings.translate("work.debugging") + "...", null); + cmd.stepOut(); + } catch (IOException ex) { + Main.getDebugHandler().disconnect(); + //ignore + } + + return true; + } + + public boolean continueActionPerformed(ActionEvent evt) { + try { + DebuggerCommands cmd = Main.getDebugHandler().getCommands(); + mainFrame.getPanel().clearDebuggerColors(); + Main.startWork(AppStrings.translate("work.debugging") + "...", null); + cmd.sendContinue(); + } catch (IOException ex) { + Main.getDebugHandler().disconnect(); + //ignore + } + + return true; + } + + public boolean stackActionPerformed(ActionEvent evt) { + //TODO + return true; + } + + public boolean watchActionPerformed(ActionEvent evt) { + //TODO + return true; + } + + public boolean dispatchKeyEvent(KeyEvent e) { + if (((JFrame) mainFrame).isActive() && e.getID() == KeyEvent.KEY_PRESSED) { + + HotKey ek = new HotKey(e); + for (String path : menuHotkeys.keySet()) { + HotKey mk = menuHotkeys.get(path); + if (ek.equals(mk)) { + if (menuActions.containsKey(path)) { + menuActions.get(path).actionPerformed(null); + return true; + } + } + } + + //other nonmenu actions + int code = e.getKeyCode(); + if (e.isControlDown() && e.isShiftDown()) { //CTRL+SHIFT + switch (code) { + case KeyEvent.VK_F: + return searchInActionPerformed(null); + case KeyEvent.VK_T: + return searchInTextPerformed(null); + case KeyEvent.VK_D: + return clearLog(null); + } + } else if (e.isControlDown() && !e.isShiftDown()) { //CTRL + switch (code) { + case KeyEvent.VK_UP: + return previousTag(null); + case KeyEvent.VK_DOWN: + return nextTag(null); + } + } + } + + return false; + } + + public abstract void hilightPath(String path); + + public abstract void setPathVisible(String path, boolean val); +} diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java b/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java index c6343785b..44f7441cc 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java @@ -1,649 +1,649 @@ -/* - * Copyright (C) 2010-2016 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.configuration.Configuration; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.swing.JCheckBox; -import javax.swing.JComponent; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JToggleButton; -import javax.swing.SwingUtilities; -import org.pushingpixels.flamingo.api.common.AbstractCommandButton; -import org.pushingpixels.flamingo.api.common.CommandButtonDisplayState; -import org.pushingpixels.flamingo.api.common.CommandToggleButtonGroup; -import org.pushingpixels.flamingo.api.common.JCommandButton; -import org.pushingpixels.flamingo.api.common.JCommandButtonPanel; -import org.pushingpixels.flamingo.api.common.JCommandToggleButton; -import org.pushingpixels.flamingo.api.common.popup.JPopupPanel; -import org.pushingpixels.flamingo.api.common.popup.PopupPanelCallback; -import org.pushingpixels.flamingo.api.ribbon.AbstractRibbonBand; -import org.pushingpixels.flamingo.api.ribbon.JRibbon; -import org.pushingpixels.flamingo.api.ribbon.JRibbonBand; -import org.pushingpixels.flamingo.api.ribbon.JRibbonComponent; -import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenu; -import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryFooter; -import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryPrimary; -import org.pushingpixels.flamingo.api.ribbon.RibbonContextualTaskGroup; -import org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority; -import org.pushingpixels.flamingo.api.ribbon.RibbonTask; -import org.pushingpixels.flamingo.api.ribbon.resize.BaseRibbonBandResizePolicy; -import org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies; -import org.pushingpixels.flamingo.api.ribbon.resize.IconRibbonBandResizePolicy; -import org.pushingpixels.flamingo.api.ribbon.resize.RibbonBandResizePolicy; -import org.pushingpixels.flamingo.internal.ui.ribbon.AbstractBandControlPanel; - -/** - * - * @author JPEXS - */ -public class MainFrameRibbonMenu extends MainFrameMenu { - - private final JRibbon ribbon; - - private final Map menuItems = new HashMap<>(); - - private final Map menuTitles = new HashMap<>(); - - private final Map menuOptional = new HashMap<>(); - - private final Map menuIcons = new HashMap<>(); - - private final Map menuLoaders = new HashMap<>(); - - private final Map menuPriorities = new HashMap<>(); - - private final Map> menuSubs = new HashMap<>(); - - private final Map menuType = new HashMap<>(); - - private final Map menuGroup = new HashMap<>(); - - private final Map menuToggleGroups = new HashMap<>(); - - private final Map menuToToggleGroup = new HashMap<>(); - - private static final int TYPE_MENU = 1; - - private static final int TYPE_MENUITEM = 2; - - private static final int TYPE_TOGGLEMENUITEM = 3; - - private final Map optionalGroups = new HashMap<>(); - - public MainFrameRibbonMenu(MainFrameRibbon mainFrame, JRibbon ribbon, boolean externalFlashPlayerUnavailable) { - super(mainFrame, externalFlashPlayerUnavailable); - this.ribbon = ribbon; - } - - private String fixCommandTitle(String title) { - if (title.length() > 2) { - if (title.charAt(1) == ' ') { - title = title.charAt(0) + "\u00A0" + title.substring(2); - } - } - return title; - } - - private RibbonBandResizePolicy titleResizePolicies(final JRibbonBand ribbonBand) { - return new BaseRibbonBandResizePolicy(ribbonBand.getControlPanel()) { - @Override - public int getPreferredWidth(int i, int i1) { - return ribbonBand.getGraphics().getFontMetrics(ribbonBand.getFont()).stringWidth(ribbonBand.getTitle()) + 20; - } - - @Override - public void install(int i, int i1) { - } - }; - } - - private List getResizePolicies(JRibbonBand ribbonBand) { - final List myResizePolicies = new ArrayList<>(); - myResizePolicies.add(new CoreRibbonResizePolicies.Mirror(ribbonBand.getControlPanel())); - myResizePolicies.add(titleResizePolicies(ribbonBand)); - myResizePolicies.add(new IconRibbonBandResizePolicy(ribbonBand.getControlPanel())); - - List resizePolicies = new ArrayList<>(); - - resizePolicies.add(new RibbonBandResizePolicy() { - - @Override - public int getPreferredWidth(int i, int i1) { - int pw = 0; - for (RibbonBandResizePolicy p : myResizePolicies) { - int npw = p.getPreferredWidth(i, i1); - if (npw > pw) { - pw = npw; - } - } - return pw; - } - - @Override - public void install(int i, int i1) { - for (RibbonBandResizePolicy p : myResizePolicies) { - p.install(i, i1); - } - } - }); - return resizePolicies; - } - - @Override - protected void loadRecent(ActionEvent evt) { - if (evt.getSource() instanceof JPanel) { - JPanel targetPanel = (JPanel) evt.getSource(); - targetPanel.removeAll(); - JCommandButtonPanel openHistoryPanel = new JCommandButtonPanel(CommandButtonDisplayState.MEDIUM); - String groupName = translate("menu.recentFiles"); - openHistoryPanel.addButtonGroup(groupName); - List recentFiles = Configuration.getRecentFiles(); - int j = 0; - for (int i = recentFiles.size() - 1; i >= 0; i--) { - String path = recentFiles.get(i); - RecentFilesButton historyButton = new RecentFilesButton(j + " " + path, null); - historyButton.fileName = path; - historyButton.addActionListener((ActionEvent ae) -> { - RecentFilesButton source = (RecentFilesButton) ae.getSource(); - if (Main.openFile(source.fileName, null) == OpenFileResult.NOT_FOUND) { - if (View.showConfirmDialog(null, translate("message.confirm.recentFileNotFound"), translate("message.confirm"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_NO_OPTION) { - Configuration.removeRecentFile(source.fileName); - } - } - }); - j++; - historyButton.setHorizontalAlignment(SwingUtilities.LEFT); - openHistoryPanel.addButtonToLastGroup(historyButton); - } - - if (recentFiles.isEmpty()) { - JCommandButton emptyLabel = new JCommandButton(translate("menu.recentFiles.empty")); - emptyLabel.setHorizontalAlignment(SwingUtilities.LEFT); - emptyLabel.setEnabled(false); - openHistoryPanel.addButtonToLastGroup(emptyLabel); - } - - openHistoryPanel.setMaxButtonColumns(1); - targetPanel.setLayout(new BorderLayout()); - targetPanel.add(openHistoryPanel, BorderLayout.CENTER); - } - } - - @Override - public void finishMenu(String path) { - if (!menuTitles.containsKey(path)) { - throw new IllegalArgumentException("Menu not started: " + path); - } - boolean isAppMenu = path.equals("_"); - String title = menuTitles.get(path); - String icon = menuIcons.get(path); - ActionListener action = menuActions.get(path); - int priority = menuPriorities.get(path); - int type = menuType.get(path); - if (type != TYPE_MENU) { - throw new IllegalArgumentException("Not a menu: " + path); - } - - String[] parts = path.contains("/") ? path.split("\\/") : new String[]{""}; - List subs = menuSubs.get(path); - - boolean onlyCheckboxes = true; - for (String sub : subs) { - if (sub.equals("-")) { - continue; - } - int subType = menuType.get(sub); - if (subType == TYPE_MENUITEM) { - onlyCheckboxes = false; - break; - } - String subIcon = menuIcons.get(sub); - if (subIcon != null) { - onlyCheckboxes = false; - break; - } - } - if (subs.isEmpty()) { - onlyCheckboxes = false; - } - - if (isAppMenu) { - RibbonApplicationMenu mainMenu = new RibbonApplicationMenu(); - for (String sub : subs) { - if (sub.equals("-")) { - mainMenu.addMenuSeparator(); - continue; - } - - String subTitle = menuTitles.get(sub); - String subIcon = menuIcons.get(sub); - int subType = menuType.get(sub); - ActionListener subAction = menuActions.get(sub); - final ActionListener subLoader = menuLoaders.get(sub); - - if (sub.startsWith("_/$")) //FooterMenu - { - RibbonApplicationMenuEntryFooter footerMenu = new RibbonApplicationMenuEntryFooter(View.getResizableIcon(subIcon, 16), subTitle, subAction); - menuItems.put(sub, footerMenu); - mainMenu.addFooterEntry(footerMenu); - } else { - RibbonApplicationMenuEntryPrimary menu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon(subIcon, 32), subTitle, subAction, - subType == TYPE_MENU ? JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION : JCommandButton.CommandButtonKind.ACTION_ONLY); - - if (subLoader != null) { - menu.setRolloverCallback(new RibbonApplicationMenuEntryPrimary.PrimaryRolloverCallback() { - @Override - public void menuEntryActivated(JPanel targetPanel) { - subLoader.actionPerformed(new ActionEvent(targetPanel, 0, "load:" + sub)); - } - }); - } - menuItems.put(sub, menu); - mainMenu.addMenuEntry(menu); - } - - } - - ribbon.setApplicationMenu(mainMenu); - return; - } - - for (String sub : subs) { - if (sub.equals("-")) { - continue; - } - int subType = menuType.get(sub); - ActionListener subAction = menuActions.get(sub); - String subTitle = menuTitles.get(sub); - String subIcon = menuIcons.get(sub); - String subGroup = menuGroup.get(sub); - HotKey subKey = menuHotkeys.get(sub); - if (subKey != null) { - String keyStr = subKey.toString(); - if (keyStr.length() < 8) { - subTitle += " (" + keyStr + ")"; - } - } - int subPriority = menuPriorities.get(sub); - final ActionListener subLoader = menuLoaders.get(sub); - AbstractCommandButton but = null; - if (subType == TYPE_MENUITEM || (subType == TYPE_MENU && subAction != null)) { - JCommandButton cbut; - if (subIcon != null) { - cbut = new JCommandButton(fixCommandTitle(subTitle), View.getResizableIcon(subIcon, subPriority == PRIORITY_TOP ? 32 : 16)); - } else { - cbut = new JCommandButton(fixCommandTitle(subTitle)); - } - if (subKey != null) { - //cbut.setActionRichTooltip(new RichTooltip(subTitle, subKey.toString())); - } - if (subLoader != null) { - cbut.setCommandButtonKind(JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION); - cbut.setPopupCallback(new PopupPanelCallback() { - - @Override - public JPopupPanel getPopupPanel(JCommandButton jcb) { - JPopupPanel jp = new JPopupPanel() { - }; - - subLoader.actionPerformed(new ActionEvent(jp, 0, "load:" + sub)); - return jp; - } - }); - } - but = cbut; - } else if (subType == TYPE_TOGGLEMENUITEM) { - if (onlyCheckboxes) { - JCheckBox cb = new JCheckBox(subTitle); - if (subAction != null) { - cb.addActionListener(subAction); - } - menuItems.put(sub, cb); - } else { - if (subIcon != null) { - but = new JCommandToggleButton(fixCommandTitle(subTitle), View.getResizableIcon(subIcon, subPriority == PRIORITY_TOP ? 32 : 16)); - } else { - but = new JCommandToggleButton(fixCommandTitle(subTitle)); - } - menuToToggleGroup.get(sub).add((JCommandToggleButton) but); - } - } - if (but != null) { - if (subAction != null) { - but.addActionListener(subAction); - } - menuItems.put(sub, but); - } - } - - //if (parts.length == 3) - { //3rd level - it's a Band! - JRibbonBand band = new JRibbonBand(title, icon != null ? View.getResizableIcon(icon, 16) : null, null); - band.setResizePolicies(getResizePolicies(band)); - int cnt = 0; - for (String sub : subs) { - if (sub.equals("-")) { - continue; - } - - Object o = menuItems.get(sub); - int subPriority = menuPriorities.get(sub); - int subType = menuType.get(sub); - ActionListener subAction = menuActions.get(sub); - if (subType != TYPE_MENU || (subAction != null)) { - if (o instanceof AbstractCommandButton) { - RibbonElementPriority ribbonPriority = RibbonElementPriority.MEDIUM; - switch (subPriority) { - case PRIORITY_LOW: - ribbonPriority = RibbonElementPriority.LOW; - break; - case PRIORITY_MEDIUM: - ribbonPriority = RibbonElementPriority.MEDIUM; - break; - case PRIORITY_TOP: - ribbonPriority = RibbonElementPriority.TOP; - break; - } - - band.addCommandButton((AbstractCommandButton) o, ribbonPriority); - cnt++; - } else if (o instanceof JComponent) { - band.addRibbonComponent(new JRibbonComponent((JComponent) o)); - cnt++; - } - } - } - if (cnt > 0) { - if (parts.length != 3) { - if (!menuSubs.containsKey(path)) { - menuSubs.put(path, new ArrayList<>()); - } - if (!menuSubs.get(path).contains(path + "/_")) { - menuSubs.get(path).add(0, path + "/_"); - } - menuItems.put(path + "/_", band); - - } else { - menuItems.put(path, band); - } - - } - - } - - if (parts.length == 1) { //1st level - it's ribbon - for (String sub : subs) { - if (sub.equals("-")) { - continue; - } - if (menuItems.get(sub) instanceof RibbonTask) { - RibbonTask rt = (RibbonTask) menuItems.get(sub); - if (menuOptional.get(sub)) { - RibbonContextualTaskGroup rct = new RibbonContextualTaskGroup("", new Color(128, 0, 0), rt); - ribbon.addContextualTaskGroup(rct); - optionalGroups.put(sub, rct); - //ribbon.setVisible(rct, false); - } else { - ribbon.addTask(rt); - } - } - } - } else if (parts.length == 2) { //2nd level - it's a Task! - int bandCount = 0; - for (String sub : subs) { - if (sub.equals("-")) { - continue; - } - - if (menuItems.get(sub) instanceof AbstractRibbonBand) { - bandCount++; - } - } - AbstractRibbonBand[] bands = new AbstractRibbonBand[bandCount]; - int b = 0; - for (String sub : subs) { - if (sub.equals("-")) { - continue; - } - if (menuItems.get(sub) instanceof AbstractRibbonBand) { - bands[b++] = (AbstractRibbonBand) menuItems.get(sub); - } - } - if (bands.length > 0) { - RibbonTask task = new RibbonTask(title, bands); - menuItems.put(path, task); - } - } - } - - @Override - public void addMenuItem(String path, String title, String icon, ActionListener action, int priority, ActionListener subLoader, boolean isLeaf, HotKey key, boolean isOptional) { - String parentPath = path.contains("/") ? path.substring(0, path.lastIndexOf('/')) : ""; - if (!menuSubs.containsKey(parentPath)) { - throw new IllegalArgumentException("No parent menu exists: " + parentPath); - } - menuOptional.put(path, isOptional); - menuHotkeys.put(path, key); - menuSubs.get(parentPath).add(path); - if (!isLeaf) { - menuSubs.put(path, new ArrayList<>()); - } - menuLoaders.put(path, subLoader); - menuTitles.put(path, title); - menuIcons.put(path, icon); - menuActions.put(path, action); - menuPriorities.put(path, priority); - menuType.put(path, isLeaf ? TYPE_MENUITEM : TYPE_MENU); - } - - @Override - public void addToggleMenuItem(String path, String title, String group, String icon, ActionListener action, int priority, HotKey key) { - addMenuItem(path, title, icon, action, priority, action, true, key, false); - menuType.put(path, TYPE_TOGGLEMENUITEM); - menuGroup.put(path, group); - if (group == null) { - group = path; - } - if (!menuToggleGroups.containsKey(group)) { - menuToggleGroups.put(group, new CommandToggleButtonGroup()); - } - menuToToggleGroup.put(path, menuToggleGroups.get(group)); - } - - @Override - public boolean isMenuChecked(String path) { - Object o = menuItems.get(path); - if (o instanceof JCommandToggleButton) { - JCommandToggleButton t = (JCommandToggleButton) o; - if (!menuToToggleGroup.containsKey(path)) { - throw new IllegalArgumentException("No toggle group for " + path); - } - return menuToToggleGroup.get(path).getSelected() == t; - } - if (o instanceof JCheckBox) { - return ((JCheckBox) o).isSelected(); - } - throw new IllegalArgumentException("Not a toggle menu"); - } - - @Override - public void setMenuChecked(String path, boolean checked) { - Object o = menuItems.get(path); - if (o instanceof JCommandToggleButton) { - JCommandToggleButton t = (JCommandToggleButton) o; - if (!menuToToggleGroup.containsKey(path)) { - throw new IllegalArgumentException("No toggle group for " + path); - } - menuToToggleGroup.get(path).setSelected(t, checked); - } else if (o instanceof JToggleButton) { - ((JToggleButton) o).setSelected(checked); - } else { - throw new IllegalArgumentException("Not a toggle menu"); - } - } - - @Override - public void setGroupSelection(String group, String selected) { - if (!menuToggleGroups.containsKey(group)) { - throw new IllegalArgumentException("Group " + group + " does not exist"); - } - menuToggleGroups.get(group).clearSelection(); - if (selected == null) { - return; - } - if (!menuItems.containsKey(selected)) { - throw new IllegalArgumentException("Selection " + selected + " not found"); - } - JCommandToggleButton c = (JCommandToggleButton) menuItems.get(selected); - menuToggleGroups.get(group).setSelected(c, true); - } - - @Override - public String getGroupSelection(String group) { - if (!menuToggleGroups.containsKey(group)) { - throw new IllegalArgumentException("Group " + group + " does not exist"); - } - JCommandToggleButton c = menuToggleGroups.get(group).getSelected(); - for (String path : menuItems.keySet()) { - if (menuItems.get(path) == c) { - return path; - } - } - return null; - } - - @Override - public void clearMenu(String path) { - - } - - @Override - public void setMenuEnabled(String path, boolean enabled) { - if (!menuItems.containsKey(path)) { - throw new IllegalArgumentException("Menu not found: " + path); - } - Object o = menuItems.get(path); - try { - if (o instanceof JRibbonBand) { - ((JRibbonBand) o).setEnabled(enabled); - } else if (o instanceof AbstractCommandButton) { - ((AbstractCommandButton) o).setEnabled(enabled); - } else if (o instanceof RibbonApplicationMenuEntryPrimary) { - ((RibbonApplicationMenuEntryPrimary) o).setEnabled(enabled); - } else if (o instanceof RibbonApplicationMenuEntryFooter) { - ((RibbonApplicationMenuEntryFooter) o).setEnabled(enabled); - } else if (o instanceof JComponent) { - ((JComponent) o).setEnabled(enabled); - } else { - throw new IllegalArgumentException("Cannot set enabled to: " + path); - } - } catch (Exception ex) { - //some substance issues, ignore - } - } - - @Override - public void initMenu() { - menuSubs.put("", new ArrayList<>()); - menuPriorities.put("", 0); - menuActions.put("", null); - menuTitles.put("", null); - menuIcons.put("", null); - menuType.put("", TYPE_MENU); - } - - @Override - public void addSeparator(String parentPath) { - if (!menuSubs.containsKey(parentPath)) { - throw new IllegalArgumentException("Menu does not exist: " + parentPath); - } - menuSubs.get(parentPath).add("-"); - } - - @Override - public boolean supportsMenuAction() { - return true; - } - - @Override - public boolean supportsAppMenu() { - return true; - } - - @Override - public void setPathVisible(String path, boolean val) { - Object o = menuItems.get(path); - if (o instanceof RibbonTask) { - if (menuOptional.get(path)) { - RibbonContextualTaskGroup rg = optionalGroups.get(path); - - if (ribbon.isVisible(rg) != val) { - View.execInEventDispatch(new Runnable() { - - @Override - public void run() { - try { - ribbon.setVisible(rg, val); - } catch (Exception ex) { - - } - } - }); - - } - } - } - } - - @Override - public void hilightPath(String path) { - Object o = menuItems.get(path); - if (o instanceof RibbonTask) { - if (menuOptional.get(path)) { - View.execInEventDispatch(new Runnable() { - - @Override - public void run() { - if (!ribbon.isVisible(optionalGroups.get(path))) { - ribbon.setVisible(optionalGroups.get(path), true); - } - ribbon.setSelectedTask((RibbonTask) o); - } - }); - return; - } - final RibbonTask rt = (RibbonTask) o; - View.execInEventDispatch(new Runnable() { - - @Override - public void run() { - ribbon.setSelectedTask(rt); - } - }); - - } - } -} +/* + * Copyright (C) 2010-2016 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.configuration.Configuration; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JToggleButton; +import javax.swing.SwingUtilities; +import org.pushingpixels.flamingo.api.common.AbstractCommandButton; +import org.pushingpixels.flamingo.api.common.CommandButtonDisplayState; +import org.pushingpixels.flamingo.api.common.CommandToggleButtonGroup; +import org.pushingpixels.flamingo.api.common.JCommandButton; +import org.pushingpixels.flamingo.api.common.JCommandButtonPanel; +import org.pushingpixels.flamingo.api.common.JCommandToggleButton; +import org.pushingpixels.flamingo.api.common.popup.JPopupPanel; +import org.pushingpixels.flamingo.api.common.popup.PopupPanelCallback; +import org.pushingpixels.flamingo.api.ribbon.AbstractRibbonBand; +import org.pushingpixels.flamingo.api.ribbon.JRibbon; +import org.pushingpixels.flamingo.api.ribbon.JRibbonBand; +import org.pushingpixels.flamingo.api.ribbon.JRibbonComponent; +import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenu; +import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryFooter; +import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryPrimary; +import org.pushingpixels.flamingo.api.ribbon.RibbonContextualTaskGroup; +import org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority; +import org.pushingpixels.flamingo.api.ribbon.RibbonTask; +import org.pushingpixels.flamingo.api.ribbon.resize.BaseRibbonBandResizePolicy; +import org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies; +import org.pushingpixels.flamingo.api.ribbon.resize.IconRibbonBandResizePolicy; +import org.pushingpixels.flamingo.api.ribbon.resize.RibbonBandResizePolicy; +import org.pushingpixels.flamingo.internal.ui.ribbon.AbstractBandControlPanel; + +/** + * + * @author JPEXS + */ +public class MainFrameRibbonMenu extends MainFrameMenu { + + private final JRibbon ribbon; + + private final Map menuItems = new HashMap<>(); + + private final Map menuTitles = new HashMap<>(); + + private final Map menuOptional = new HashMap<>(); + + private final Map menuIcons = new HashMap<>(); + + private final Map menuLoaders = new HashMap<>(); + + private final Map menuPriorities = new HashMap<>(); + + private final Map> menuSubs = new HashMap<>(); + + private final Map menuType = new HashMap<>(); + + private final Map menuGroup = new HashMap<>(); + + private final Map menuToggleGroups = new HashMap<>(); + + private final Map menuToToggleGroup = new HashMap<>(); + + private static final int TYPE_MENU = 1; + + private static final int TYPE_MENUITEM = 2; + + private static final int TYPE_TOGGLEMENUITEM = 3; + + private final Map optionalGroups = new HashMap<>(); + + public MainFrameRibbonMenu(MainFrameRibbon mainFrame, JRibbon ribbon, boolean externalFlashPlayerUnavailable) { + super(mainFrame, externalFlashPlayerUnavailable); + this.ribbon = ribbon; + } + + private String fixCommandTitle(String title) { + if (title.length() > 2) { + if (title.charAt(1) == ' ') { + title = title.charAt(0) + "\u00A0" + title.substring(2); + } + } + return title; + } + + private RibbonBandResizePolicy titleResizePolicies(final JRibbonBand ribbonBand) { + return new BaseRibbonBandResizePolicy(ribbonBand.getControlPanel()) { + @Override + public int getPreferredWidth(int i, int i1) { + return ribbonBand.getGraphics().getFontMetrics(ribbonBand.getFont()).stringWidth(ribbonBand.getTitle()) + 20; + } + + @Override + public void install(int i, int i1) { + } + }; + } + + private List getResizePolicies(JRibbonBand ribbonBand) { + final List myResizePolicies = new ArrayList<>(); + myResizePolicies.add(new CoreRibbonResizePolicies.Mirror(ribbonBand.getControlPanel())); + myResizePolicies.add(titleResizePolicies(ribbonBand)); + myResizePolicies.add(new IconRibbonBandResizePolicy(ribbonBand.getControlPanel())); + + List resizePolicies = new ArrayList<>(); + + resizePolicies.add(new RibbonBandResizePolicy() { + + @Override + public int getPreferredWidth(int i, int i1) { + int pw = 0; + for (RibbonBandResizePolicy p : myResizePolicies) { + int npw = p.getPreferredWidth(i, i1); + if (npw > pw) { + pw = npw; + } + } + return pw; + } + + @Override + public void install(int i, int i1) { + for (RibbonBandResizePolicy p : myResizePolicies) { + p.install(i, i1); + } + } + }); + return resizePolicies; + } + + @Override + protected void loadRecent(ActionEvent evt) { + if (evt.getSource() instanceof JPanel) { + JPanel targetPanel = (JPanel) evt.getSource(); + targetPanel.removeAll(); + JCommandButtonPanel openHistoryPanel = new JCommandButtonPanel(CommandButtonDisplayState.MEDIUM); + String groupName = translate("menu.recentFiles"); + openHistoryPanel.addButtonGroup(groupName); + List recentFiles = Configuration.getRecentFiles(); + int j = 0; + for (int i = recentFiles.size() - 1; i >= 0; i--) { + String path = recentFiles.get(i); + RecentFilesButton historyButton = new RecentFilesButton(j + " " + path, null); + historyButton.fileName = path; + historyButton.addActionListener((ActionEvent ae) -> { + RecentFilesButton source = (RecentFilesButton) ae.getSource(); + if (Main.openFile(source.fileName, null) == OpenFileResult.NOT_FOUND) { + if (View.showConfirmDialog(null, translate("message.confirm.recentFileNotFound"), translate("message.confirm"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_NO_OPTION) { + Configuration.removeRecentFile(source.fileName); + } + } + }); + j++; + historyButton.setHorizontalAlignment(SwingUtilities.LEFT); + openHistoryPanel.addButtonToLastGroup(historyButton); + } + + if (recentFiles.isEmpty()) { + JCommandButton emptyLabel = new JCommandButton(translate("menu.recentFiles.empty")); + emptyLabel.setHorizontalAlignment(SwingUtilities.LEFT); + emptyLabel.setEnabled(false); + openHistoryPanel.addButtonToLastGroup(emptyLabel); + } + + openHistoryPanel.setMaxButtonColumns(1); + targetPanel.setLayout(new BorderLayout()); + targetPanel.add(openHistoryPanel, BorderLayout.CENTER); + } + } + + @Override + public void finishMenu(String path) { + if (!menuTitles.containsKey(path)) { + throw new IllegalArgumentException("Menu not started: " + path); + } + boolean isAppMenu = path.equals("_"); + String title = menuTitles.get(path); + String icon = menuIcons.get(path); + ActionListener action = menuActions.get(path); + int priority = menuPriorities.get(path); + int type = menuType.get(path); + if (type != TYPE_MENU) { + throw new IllegalArgumentException("Not a menu: " + path); + } + + String[] parts = path.contains("/") ? path.split("\\/") : new String[]{""}; + List subs = menuSubs.get(path); + + boolean onlyCheckboxes = true; + for (String sub : subs) { + if (sub.equals("-")) { + continue; + } + int subType = menuType.get(sub); + if (subType == TYPE_MENUITEM) { + onlyCheckboxes = false; + break; + } + String subIcon = menuIcons.get(sub); + if (subIcon != null) { + onlyCheckboxes = false; + break; + } + } + if (subs.isEmpty()) { + onlyCheckboxes = false; + } + + if (isAppMenu) { + RibbonApplicationMenu mainMenu = new RibbonApplicationMenu(); + for (String sub : subs) { + if (sub.equals("-")) { + mainMenu.addMenuSeparator(); + continue; + } + + String subTitle = menuTitles.get(sub); + String subIcon = menuIcons.get(sub); + int subType = menuType.get(sub); + ActionListener subAction = menuActions.get(sub); + final ActionListener subLoader = menuLoaders.get(sub); + + if (sub.startsWith("_/$")) //FooterMenu + { + RibbonApplicationMenuEntryFooter footerMenu = new RibbonApplicationMenuEntryFooter(View.getResizableIcon(subIcon, 16), subTitle, subAction); + menuItems.put(sub, footerMenu); + mainMenu.addFooterEntry(footerMenu); + } else { + RibbonApplicationMenuEntryPrimary menu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon(subIcon, 32), subTitle, subAction, + subType == TYPE_MENU ? JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION : JCommandButton.CommandButtonKind.ACTION_ONLY); + + if (subLoader != null) { + menu.setRolloverCallback(new RibbonApplicationMenuEntryPrimary.PrimaryRolloverCallback() { + @Override + public void menuEntryActivated(JPanel targetPanel) { + subLoader.actionPerformed(new ActionEvent(targetPanel, 0, "load:" + sub)); + } + }); + } + menuItems.put(sub, menu); + mainMenu.addMenuEntry(menu); + } + + } + + ribbon.setApplicationMenu(mainMenu); + return; + } + + for (String sub : subs) { + if (sub.equals("-")) { + continue; + } + int subType = menuType.get(sub); + ActionListener subAction = menuActions.get(sub); + String subTitle = menuTitles.get(sub); + String subIcon = menuIcons.get(sub); + String subGroup = menuGroup.get(sub); + HotKey subKey = menuHotkeys.get(sub); + if (subKey != null) { + String keyStr = subKey.toString(); + if (keyStr.length() < 8) { + subTitle += " (" + keyStr + ")"; + } + } + int subPriority = menuPriorities.get(sub); + final ActionListener subLoader = menuLoaders.get(sub); + AbstractCommandButton but = null; + if (subType == TYPE_MENUITEM || (subType == TYPE_MENU && subAction != null)) { + JCommandButton cbut; + if (subIcon != null) { + cbut = new JCommandButton(fixCommandTitle(subTitle), View.getResizableIcon(subIcon, subPriority == PRIORITY_TOP ? 32 : 16)); + } else { + cbut = new JCommandButton(fixCommandTitle(subTitle)); + } + if (subKey != null) { + //cbut.setActionRichTooltip(new RichTooltip(subTitle, subKey.toString())); + } + if (subLoader != null) { + cbut.setCommandButtonKind(JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION); + cbut.setPopupCallback(new PopupPanelCallback() { + + @Override + public JPopupPanel getPopupPanel(JCommandButton jcb) { + JPopupPanel jp = new JPopupPanel() { + }; + + subLoader.actionPerformed(new ActionEvent(jp, 0, "load:" + sub)); + return jp; + } + }); + } + but = cbut; + } else if (subType == TYPE_TOGGLEMENUITEM) { + if (onlyCheckboxes) { + JCheckBox cb = new JCheckBox(subTitle); + if (subAction != null) { + cb.addActionListener(subAction); + } + menuItems.put(sub, cb); + } else { + if (subIcon != null) { + but = new JCommandToggleButton(fixCommandTitle(subTitle), View.getResizableIcon(subIcon, subPriority == PRIORITY_TOP ? 32 : 16)); + } else { + but = new JCommandToggleButton(fixCommandTitle(subTitle)); + } + menuToToggleGroup.get(sub).add((JCommandToggleButton) but); + } + } + if (but != null) { + if (subAction != null) { + but.addActionListener(subAction); + } + menuItems.put(sub, but); + } + } + + //if (parts.length == 3) + { //3rd level - it's a Band! + JRibbonBand band = new JRibbonBand(title, icon != null ? View.getResizableIcon(icon, 16) : null, null); + band.setResizePolicies(getResizePolicies(band)); + int cnt = 0; + for (String sub : subs) { + if (sub.equals("-")) { + continue; + } + + Object o = menuItems.get(sub); + int subPriority = menuPriorities.get(sub); + int subType = menuType.get(sub); + ActionListener subAction = menuActions.get(sub); + if (subType != TYPE_MENU || (subAction != null)) { + if (o instanceof AbstractCommandButton) { + RibbonElementPriority ribbonPriority = RibbonElementPriority.MEDIUM; + switch (subPriority) { + case PRIORITY_LOW: + ribbonPriority = RibbonElementPriority.LOW; + break; + case PRIORITY_MEDIUM: + ribbonPriority = RibbonElementPriority.MEDIUM; + break; + case PRIORITY_TOP: + ribbonPriority = RibbonElementPriority.TOP; + break; + } + + band.addCommandButton((AbstractCommandButton) o, ribbonPriority); + cnt++; + } else if (o instanceof JComponent) { + band.addRibbonComponent(new JRibbonComponent((JComponent) o)); + cnt++; + } + } + } + if (cnt > 0) { + if (parts.length != 3) { + if (!menuSubs.containsKey(path)) { + menuSubs.put(path, new ArrayList<>()); + } + if (!menuSubs.get(path).contains(path + "/_")) { + menuSubs.get(path).add(0, path + "/_"); + } + menuItems.put(path + "/_", band); + + } else { + menuItems.put(path, band); + } + + } + + } + + if (parts.length == 1) { //1st level - it's ribbon + for (String sub : subs) { + if (sub.equals("-")) { + continue; + } + if (menuItems.get(sub) instanceof RibbonTask) { + RibbonTask rt = (RibbonTask) menuItems.get(sub); + if (menuOptional.get(sub)) { + RibbonContextualTaskGroup rct = new RibbonContextualTaskGroup("", new Color(128, 0, 0), rt); + ribbon.addContextualTaskGroup(rct); + optionalGroups.put(sub, rct); + //ribbon.setVisible(rct, false); + } else { + ribbon.addTask(rt); + } + } + } + } else if (parts.length == 2) { //2nd level - it's a Task! + int bandCount = 0; + for (String sub : subs) { + if (sub.equals("-")) { + continue; + } + + if (menuItems.get(sub) instanceof AbstractRibbonBand) { + bandCount++; + } + } + AbstractRibbonBand[] bands = new AbstractRibbonBand[bandCount]; + int b = 0; + for (String sub : subs) { + if (sub.equals("-")) { + continue; + } + if (menuItems.get(sub) instanceof AbstractRibbonBand) { + bands[b++] = (AbstractRibbonBand) menuItems.get(sub); + } + } + if (bands.length > 0) { + RibbonTask task = new RibbonTask(title, bands); + menuItems.put(path, task); + } + } + } + + @Override + public void addMenuItem(String path, String title, String icon, ActionListener action, int priority, ActionListener subLoader, boolean isLeaf, HotKey key, boolean isOptional) { + String parentPath = path.contains("/") ? path.substring(0, path.lastIndexOf('/')) : ""; + if (!menuSubs.containsKey(parentPath)) { + throw new IllegalArgumentException("No parent menu exists: " + parentPath); + } + menuOptional.put(path, isOptional); + menuHotkeys.put(path, key); + menuSubs.get(parentPath).add(path); + if (!isLeaf) { + menuSubs.put(path, new ArrayList<>()); + } + menuLoaders.put(path, subLoader); + menuTitles.put(path, title); + menuIcons.put(path, icon); + menuActions.put(path, action); + menuPriorities.put(path, priority); + menuType.put(path, isLeaf ? TYPE_MENUITEM : TYPE_MENU); + } + + @Override + public void addToggleMenuItem(String path, String title, String group, String icon, ActionListener action, int priority, HotKey key) { + addMenuItem(path, title, icon, action, priority, action, true, key, false); + menuType.put(path, TYPE_TOGGLEMENUITEM); + menuGroup.put(path, group); + if (group == null) { + group = path; + } + if (!menuToggleGroups.containsKey(group)) { + menuToggleGroups.put(group, new CommandToggleButtonGroup()); + } + menuToToggleGroup.put(path, menuToggleGroups.get(group)); + } + + @Override + public boolean isMenuChecked(String path) { + Object o = menuItems.get(path); + if (o instanceof JCommandToggleButton) { + JCommandToggleButton t = (JCommandToggleButton) o; + if (!menuToToggleGroup.containsKey(path)) { + throw new IllegalArgumentException("No toggle group for " + path); + } + return menuToToggleGroup.get(path).getSelected() == t; + } + if (o instanceof JCheckBox) { + return ((JCheckBox) o).isSelected(); + } + throw new IllegalArgumentException("Not a toggle menu"); + } + + @Override + public void setMenuChecked(String path, boolean checked) { + Object o = menuItems.get(path); + if (o instanceof JCommandToggleButton) { + JCommandToggleButton t = (JCommandToggleButton) o; + if (!menuToToggleGroup.containsKey(path)) { + throw new IllegalArgumentException("No toggle group for " + path); + } + menuToToggleGroup.get(path).setSelected(t, checked); + } else if (o instanceof JToggleButton) { + ((JToggleButton) o).setSelected(checked); + } else { + throw new IllegalArgumentException("Not a toggle menu"); + } + } + + @Override + public void setGroupSelection(String group, String selected) { + if (!menuToggleGroups.containsKey(group)) { + throw new IllegalArgumentException("Group " + group + " does not exist"); + } + menuToggleGroups.get(group).clearSelection(); + if (selected == null) { + return; + } + if (!menuItems.containsKey(selected)) { + throw new IllegalArgumentException("Selection " + selected + " not found"); + } + JCommandToggleButton c = (JCommandToggleButton) menuItems.get(selected); + menuToggleGroups.get(group).setSelected(c, true); + } + + @Override + public String getGroupSelection(String group) { + if (!menuToggleGroups.containsKey(group)) { + throw new IllegalArgumentException("Group " + group + " does not exist"); + } + JCommandToggleButton c = menuToggleGroups.get(group).getSelected(); + for (String path : menuItems.keySet()) { + if (menuItems.get(path) == c) { + return path; + } + } + return null; + } + + @Override + public void clearMenu(String path) { + + } + + @Override + public void setMenuEnabled(String path, boolean enabled) { + if (!menuItems.containsKey(path)) { + throw new IllegalArgumentException("Menu not found: " + path); + } + Object o = menuItems.get(path); + try { + if (o instanceof JRibbonBand) { + ((JRibbonBand) o).setEnabled(enabled); + } else if (o instanceof AbstractCommandButton) { + ((AbstractCommandButton) o).setEnabled(enabled); + } else if (o instanceof RibbonApplicationMenuEntryPrimary) { + ((RibbonApplicationMenuEntryPrimary) o).setEnabled(enabled); + } else if (o instanceof RibbonApplicationMenuEntryFooter) { + ((RibbonApplicationMenuEntryFooter) o).setEnabled(enabled); + } else if (o instanceof JComponent) { + ((JComponent) o).setEnabled(enabled); + } else { + throw new IllegalArgumentException("Cannot set enabled to: " + path); + } + } catch (Exception ex) { + //some substance issues, ignore + } + } + + @Override + public void initMenu() { + menuSubs.put("", new ArrayList<>()); + menuPriorities.put("", 0); + menuActions.put("", null); + menuTitles.put("", null); + menuIcons.put("", null); + menuType.put("", TYPE_MENU); + } + + @Override + public void addSeparator(String parentPath) { + if (!menuSubs.containsKey(parentPath)) { + throw new IllegalArgumentException("Menu does not exist: " + parentPath); + } + menuSubs.get(parentPath).add("-"); + } + + @Override + public boolean supportsMenuAction() { + return true; + } + + @Override + public boolean supportsAppMenu() { + return true; + } + + @Override + public void setPathVisible(String path, boolean val) { + Object o = menuItems.get(path); + if (o instanceof RibbonTask) { + if (menuOptional.get(path)) { + RibbonContextualTaskGroup rg = optionalGroups.get(path); + + if (ribbon.isVisible(rg) != val) { + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + try { + ribbon.setVisible(rg, val); + } catch (Exception ex) { + + } + } + }); + + } + } + } + } + + @Override + public void hilightPath(String path) { + Object o = menuItems.get(path); + if (o instanceof RibbonTask) { + if (menuOptional.get(path)) { + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + if (!ribbon.isVisible(optionalGroups.get(path))) { + ribbon.setVisible(optionalGroups.get(path), true); + } + ribbon.setSelectedTask((RibbonTask) o); + } + }); + return; + } + final RibbonTask rt = (RibbonTask) o; + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + ribbon.setSelectedTask(rt); + } + }); + + } + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/MenuBuilder.java b/src/com/jpexs/decompiler/flash/gui/MenuBuilder.java index 6443ad35a..29da062ab 100644 --- a/src/com/jpexs/decompiler/flash/gui/MenuBuilder.java +++ b/src/com/jpexs/decompiler/flash/gui/MenuBuilder.java @@ -1,293 +1,293 @@ -/* - * Copyright (C) 2010-2016 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 java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.Map; - -/** - * Menu Builder. Creates menu. - * - * @author JPEXS - */ -public interface MenuBuilder { - - public static class HotKey { - - private static Map keyCodesToNames = new HashMap<>(); - - private static Map keyNamesToCodes = new HashMap<>(); - - { - - Field[] fields = KeyEvent.class.getFields(); - - for (int i = 0; i < fields.length; i++) { - - String fieldName = fields[i].getName(); - - // We only care about the field names corresponding to key codes - if (fieldName.startsWith("VK")) { - try { - int keyCode = fields[i].getInt(null); - String keyName = fieldName.substring(3); - keyCodesToNames.put(keyCode, keyName); - keyNamesToCodes.put(keyName, keyCode); - } catch (Exception ex) { - - } - } - } - } - - public int key; - - public boolean shiftDown; - - public boolean ctrlDown; - - public boolean altDown; - - @Override - public int hashCode() { - int hash = 7; - hash = 41 * hash + this.key; - hash = 41 * hash + (this.shiftDown ? 1 : 0); - hash = 41 * hash + (this.ctrlDown ? 1 : 0); - hash = 41 * hash + (this.altDown ? 1 : 0); - return hash; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final HotKey other = (HotKey) obj; - if (this.key != other.key) { - return false; - } - if (this.shiftDown != other.shiftDown) { - return false; - } - if (this.ctrlDown != other.ctrlDown) { - return false; - } - return (this.altDown == other.altDown); - } - - public boolean matches(KeyEvent ev) { - return ev.getKeyCode() == key && ev.isControlDown() == ctrlDown && ev.isShiftDown() == shiftDown && ev.isAltDown() == altDown; - } - - public int getModifier() { - return (shiftDown ? KeyEvent.SHIFT_MASK : 0) + (ctrlDown ? KeyEvent.CTRL_MASK : 0) + (altDown ? KeyEvent.ALT_MASK : 0); - } - - public HotKey(String h) { - String[] parts = h.contains("+") ? h.split("\\+") : new String[]{h}; - for (String s : parts) { - switch (s) { - case "SHIFT": - shiftDown = true; - break; - case "CTRL": - ctrlDown = true; - break; - case "ALT": - altDown = true; - break; - default: - if (keyNamesToCodes.containsKey(s)) { - key = keyNamesToCodes.get(s); - } else { - throw new IllegalArgumentException("Key " + s + " not found!"); - } - } - } - } - - public HotKey(KeyEvent ev) { - this(ev.getKeyCode(), ev.isShiftDown(), ev.isControlDown(), ev.isAltDown()); - } - - public HotKey(int key) { - this(key, false, false, false); - } - - public HotKey(int key, boolean shiftDown, boolean ctrlDown, boolean altDown) { - this.key = key; - this.shiftDown = shiftDown; - this.ctrlDown = ctrlDown; - this.altDown = altDown; - } - - @Override - public String toString() { - String s = ""; - if (shiftDown) { - s += "SHIFT"; - } - if (ctrlDown) { - if (!s.isEmpty()) { - s += "+"; - } - s += "CTRL"; - } - if (altDown) { - if (!s.isEmpty()) { - s += "+"; - } - s += "ALT"; - } - if (!s.isEmpty()) { - s += "+"; - } - s += keyCodesToNames.get(key); - - return s; - } - } - - public static final int PRIORITY_LOW = 1; - - public static final int PRIORITY_MEDIUM = 2; - - public static final int PRIORITY_TOP = 3; - - /** - * Initializes menuBuilder - */ - public void initMenu(); - - /** - * Adds separator - * - * @param parentPath Parent menu path - */ - public void addSeparator(String parentPath); - - /** - * Adds menu item - * - * @param path Path - * @param title Title - * @param icon Icon - resource name - * @param action Action for clicking - * @param priority Priority - * @param subloader Action which loads menu inside - * @param isLeaf Has no subitems? - * @param key - * @param isOptional - */ - public void addMenuItem(String path, String title, String icon, ActionListener action, int priority, ActionListener subloader, boolean isLeaf, HotKey key, boolean isOptional); - - /** - * Adds toggle item (radio/checkbox) - * - * @param path Path - * @param title Title - * @param group Group for toggling. Null = checkbox - * @param icon Icon - resource name - * @param action Action for clicking - * @param priority Priority - * @param key - */ - public void addToggleMenuItem(String path, String title, String group, String icon, ActionListener action, int priority, HotKey key); - - /** - * Test menu checked (toggle) - * - * @param path Menu path - * @return True when checked - */ - public boolean isMenuChecked(String path); - - /** - * Hotkey for menu - * - * @param path Menu path - * @return - */ - public HotKey getMenuHotkey(String path); - - /** - * Sets menu checked (toggle) - * - * @param path Menu path - * @param checked Checked? - */ - public void setMenuChecked(String path, boolean checked); - - /** - * Sets checked item from group - * - * @param group Group name - * @param selected Selected path - */ - public void setGroupSelection(String group, String selected); - - /** - * Gets selected path from group - * - * @param group Group name - * @return Path - */ - public String getGroupSelection(String group); - - /** - * Clears menu - * - * @param path Path of menu - */ - public void clearMenu(String path); - - /** - * Sets enabled property of menu - * - * @param path Menu path - * @param enabled Enabled? - */ - public void setMenuEnabled(String path, boolean enabled); - - /** - * Finished creating menu - * - * @param path Menu path - */ - public void finishMenu(String path); - - /** - * Does this builder support actions for menus? (not menuitems) - * - * @return True if supports - */ - public boolean supportsMenuAction(); - - /** - * Does this builder support application menu (Path "_/...") - * - * @return True if supports - */ - public boolean supportsAppMenu(); -} +/* + * Copyright (C) 2010-2016 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 java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +/** + * Menu Builder. Creates menu. + * + * @author JPEXS + */ +public interface MenuBuilder { + + public static class HotKey { + + private static Map keyCodesToNames = new HashMap<>(); + + private static Map keyNamesToCodes = new HashMap<>(); + + { + + Field[] fields = KeyEvent.class.getFields(); + + for (int i = 0; i < fields.length; i++) { + + String fieldName = fields[i].getName(); + + // We only care about the field names corresponding to key codes + if (fieldName.startsWith("VK")) { + try { + int keyCode = fields[i].getInt(null); + String keyName = fieldName.substring(3); + keyCodesToNames.put(keyCode, keyName); + keyNamesToCodes.put(keyName, keyCode); + } catch (Exception ex) { + + } + } + } + } + + public int key; + + public boolean shiftDown; + + public boolean ctrlDown; + + public boolean altDown; + + @Override + public int hashCode() { + int hash = 7; + hash = 41 * hash + this.key; + hash = 41 * hash + (this.shiftDown ? 1 : 0); + hash = 41 * hash + (this.ctrlDown ? 1 : 0); + hash = 41 * hash + (this.altDown ? 1 : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final HotKey other = (HotKey) obj; + if (this.key != other.key) { + return false; + } + if (this.shiftDown != other.shiftDown) { + return false; + } + if (this.ctrlDown != other.ctrlDown) { + return false; + } + return (this.altDown == other.altDown); + } + + public boolean matches(KeyEvent ev) { + return ev.getKeyCode() == key && ev.isControlDown() == ctrlDown && ev.isShiftDown() == shiftDown && ev.isAltDown() == altDown; + } + + public int getModifier() { + return (shiftDown ? KeyEvent.SHIFT_MASK : 0) + (ctrlDown ? KeyEvent.CTRL_MASK : 0) + (altDown ? KeyEvent.ALT_MASK : 0); + } + + public HotKey(String h) { + String[] parts = h.contains("+") ? h.split("\\+") : new String[]{h}; + for (String s : parts) { + switch (s) { + case "SHIFT": + shiftDown = true; + break; + case "CTRL": + ctrlDown = true; + break; + case "ALT": + altDown = true; + break; + default: + if (keyNamesToCodes.containsKey(s)) { + key = keyNamesToCodes.get(s); + } else { + throw new IllegalArgumentException("Key " + s + " not found!"); + } + } + } + } + + public HotKey(KeyEvent ev) { + this(ev.getKeyCode(), ev.isShiftDown(), ev.isControlDown(), ev.isAltDown()); + } + + public HotKey(int key) { + this(key, false, false, false); + } + + public HotKey(int key, boolean shiftDown, boolean ctrlDown, boolean altDown) { + this.key = key; + this.shiftDown = shiftDown; + this.ctrlDown = ctrlDown; + this.altDown = altDown; + } + + @Override + public String toString() { + String s = ""; + if (shiftDown) { + s += "SHIFT"; + } + if (ctrlDown) { + if (!s.isEmpty()) { + s += "+"; + } + s += "CTRL"; + } + if (altDown) { + if (!s.isEmpty()) { + s += "+"; + } + s += "ALT"; + } + if (!s.isEmpty()) { + s += "+"; + } + s += keyCodesToNames.get(key); + + return s; + } + } + + public static final int PRIORITY_LOW = 1; + + public static final int PRIORITY_MEDIUM = 2; + + public static final int PRIORITY_TOP = 3; + + /** + * Initializes menuBuilder + */ + public void initMenu(); + + /** + * Adds separator + * + * @param parentPath Parent menu path + */ + public void addSeparator(String parentPath); + + /** + * Adds menu item + * + * @param path Path + * @param title Title + * @param icon Icon - resource name + * @param action Action for clicking + * @param priority Priority + * @param subloader Action which loads menu inside + * @param isLeaf Has no subitems? + * @param key + * @param isOptional + */ + public void addMenuItem(String path, String title, String icon, ActionListener action, int priority, ActionListener subloader, boolean isLeaf, HotKey key, boolean isOptional); + + /** + * Adds toggle item (radio/checkbox) + * + * @param path Path + * @param title Title + * @param group Group for toggling. Null = checkbox + * @param icon Icon - resource name + * @param action Action for clicking + * @param priority Priority + * @param key + */ + public void addToggleMenuItem(String path, String title, String group, String icon, ActionListener action, int priority, HotKey key); + + /** + * Test menu checked (toggle) + * + * @param path Menu path + * @return True when checked + */ + public boolean isMenuChecked(String path); + + /** + * Hotkey for menu + * + * @param path Menu path + * @return + */ + public HotKey getMenuHotkey(String path); + + /** + * Sets menu checked (toggle) + * + * @param path Menu path + * @param checked Checked? + */ + public void setMenuChecked(String path, boolean checked); + + /** + * Sets checked item from group + * + * @param group Group name + * @param selected Selected path + */ + public void setGroupSelection(String group, String selected); + + /** + * Gets selected path from group + * + * @param group Group name + * @return Path + */ + public String getGroupSelection(String group); + + /** + * Clears menu + * + * @param path Path of menu + */ + public void clearMenu(String path); + + /** + * Sets enabled property of menu + * + * @param path Menu path + * @param enabled Enabled? + */ + public void setMenuEnabled(String path, boolean enabled); + + /** + * Finished creating menu + * + * @param path Menu path + */ + public void finishMenu(String path); + + /** + * Does this builder support actions for menus? (not menuitems) + * + * @return True if supports + */ + public boolean supportsMenuAction(); + + /** + * Does this builder support application menu (Path "_/...") + * + * @return True if supports + */ + public boolean supportsAppMenu(); +} diff --git a/src/com/jpexs/decompiler/flash/gui/View.java b/src/com/jpexs/decompiler/flash/gui/View.java index b34802995..add04b510 100644 --- a/src/com/jpexs/decompiler/flash/gui/View.java +++ b/src/com/jpexs/decompiler/flash/gui/View.java @@ -1,768 +1,768 @@ -/* - * Copyright (C) 2010-2016 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.configuration.Configuration; -import com.jpexs.decompiler.flash.configuration.ConfigurationItem; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Desktop; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.GraphicsConfiguration; -import java.awt.GraphicsDevice; -import java.awt.GraphicsEnvironment; -import java.awt.Image; -import java.awt.Rectangle; -import java.awt.SystemColor; -import java.awt.TexturePaint; -import java.awt.Window; -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -import java.awt.event.WindowEvent; -import java.awt.image.BufferedImage; -import java.awt.image.VolatileImage; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.imageio.ImageIO; -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.ActionMap; -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.InputMap; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JEditorPane; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JRootPane; -import javax.swing.JTable; -import javax.swing.JTree; -import javax.swing.KeyStroke; -import javax.swing.LookAndFeel; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; -import javax.swing.plaf.FontUIResource; -import javax.swing.plaf.basic.BasicColorChooserUI; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableColumnModel; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; -import javax.swing.table.TableModel; -import javax.swing.text.JTextComponent; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; -import org.pushingpixels.flamingo.api.common.icon.ImageWrapperResizableIcon; -import org.pushingpixels.substance.api.ColorSchemeAssociationKind; -import org.pushingpixels.substance.api.ComponentState; -import org.pushingpixels.substance.api.DecorationAreaType; -import org.pushingpixels.substance.api.SubstanceColorScheme; -import org.pushingpixels.substance.api.SubstanceConstants; -import org.pushingpixels.substance.api.SubstanceLookAndFeel; -import org.pushingpixels.substance.api.SubstanceSkin; -import org.pushingpixels.substance.api.fonts.FontPolicy; -import org.pushingpixels.substance.api.fonts.FontSet; -import org.pushingpixels.substance.api.skin.SubstanceOfficeBlue2007LookAndFeel; -import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities; - -/** - * Contains methods for GUI - * - * @author JPEXS - */ -public class View { - - public static Color getDefaultBackgroundColor() { - if (Configuration.useRibbonInterface.get()) { - return SubstanceLookAndFeel.getCurrentSkin().getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.FILL, ComponentState.ENABLED).getBackgroundFillColor(); - } else { - return SystemColor.control; - } - } - - private static Color swfBackgroundColor = null; - - public static void setSwfBackgroundColor(Color swfBackgroundColor) { - View.swfBackgroundColor = swfBackgroundColor; - } - - public static Color getSwfBackgroundColor() { - if (swfBackgroundColor == null) { - return getDefaultBackgroundColor(); - } - return swfBackgroundColor; - } - - private static final BufferedImage transparentTexture; - - public static final TexturePaint transparentPaint; - - private static final Color transparentColor1 = new Color(0x99, 0x99, 0x99); - - private static final Color transparentColor2 = new Color(0x66, 0x66, 0x66); - - static { - transparentTexture = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); - Graphics g = transparentTexture.getGraphics(); - g.setColor(transparentColor1); - g.fillRect(0, 0, 16, 16); - g.setColor(transparentColor2); - g.fillRect(0, 0, 8, 8); - g.fillRect(8, 8, 8, 8); - transparentPaint = new TexturePaint(View.transparentTexture, new Rectangle(0, 0, transparentTexture.getWidth(), transparentTexture.getHeight())); - } - - /** - * Sets windows Look and Feel - */ - public static void setLookAndFeel() { - - //Save default font for Chinese characters - final Font defaultFont = (new JLabel()).getFont(); - try { - - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - - } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) { - } - - try { - LookAndFeel oldLookAndFeel = UIManager.getLookAndFeel(); - if (!(oldLookAndFeel instanceof SubstanceOfficeBlue2007LookAndFeel)) { - UIManager.setLookAndFeel(new SubstanceOfficeBlue2007LookAndFeel()); - oldLookAndFeel.uninitialize(); - } - - SubstanceSkin currentSkin = SubstanceLookAndFeel.getCurrentSkin(); - if (currentSkin != null) { - String currentSkinName = currentSkin.getClass().getName(); - String newSkinName = Configuration.guiSkin.get(); - if (!currentSkinName.equals(newSkinName)) { - SubstanceLookAndFeel.setSkin(newSkinName); - } - } else { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, "Current skin is null"); - SubstanceLookAndFeel.setSkin("com.jpexs.decompiler.flash.gui.OceanicSkin"); - } - - UIManager.put(SubstanceLookAndFeel.COLORIZATION_FACTOR, 0.999);//This works for not changing labels color and not changing Dialogs title - UIManager.put("Tree.expandedIcon", getIcon("expand16")); - UIManager.put("Tree.collapsedIcon", getIcon("collapse16")); - UIManager.put("ColorChooserUI", BasicColorChooserUI.class.getName()); - UIManager.put("ColorChooser.swatchesRecentSwatchSize", new Dimension(20, 20)); - UIManager.put("ColorChooser.swatchesSwatchSize", new Dimension(20, 20)); - UIManager.put("RibbonApplicationMenuPopupPanelUI", MyRibbonApplicationMenuPopupPanelUI.class.getName()); - UIManager.put("RibbonApplicationMenuButtonUI", MyRibbonApplicationMenuButtonUI.class.getName()); - UIManager.put("ProgressBarUI", MyProgressBarUI.class.getName()); - UIManager.put("TextField.background", Color.white); - UIManager.put("FormattedTextField.background", Color.white); - UIManager.put("CommandButtonUI", MyCommandButtonUI.class.getName()); - - FontPolicy pol = SubstanceLookAndFeel.getFontPolicy(); - final FontSet fs = pol.getFontSet("Substance", null); - - //Restore default font for chinese characters - SubstanceLookAndFeel.setFontPolicy(new FontPolicy() { - - private final FontSet fontSet = new FontSet() { - - private FontUIResource controlFont; - - private FontUIResource menuFont; - - private FontUIResource titleFont; - - private FontUIResource windowTitleFont; - - private FontUIResource smallFont; - - private FontUIResource messageFont; - - @Override - public FontUIResource getControlFont() { - if (controlFont == null) { - FontUIResource f = fs.getControlFont(); - controlFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return controlFont; - } - - @Override - public FontUIResource getMenuFont() { - if (menuFont == null) { - FontUIResource f = fs.getMenuFont(); - menuFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return menuFont; - } - - @Override - public FontUIResource getTitleFont() { - if (titleFont == null) { - FontUIResource f = fs.getTitleFont(); - titleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return titleFont; - } - - @Override - public FontUIResource getWindowTitleFont() { - if (windowTitleFont == null) { - FontUIResource f = fs.getWindowTitleFont(); - windowTitleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return windowTitleFont; - } - - @Override - public FontUIResource getSmallFont() { - if (smallFont == null) { - FontUIResource f = fs.getSmallFont(); - smallFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return smallFont; - } - - @Override - public FontUIResource getMessageFont() { - if (messageFont == null) { - FontUIResource f = fs.getMessageFont(); - messageFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return messageFont; - } - }; - - @Override - public FontSet getFontSet(String string, UIDefaults uid) { - return fontSet; - } - }); - } catch (UnsupportedLookAndFeelException ex) { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); - } - - UIManager.put(SubstanceLookAndFeel.TABBED_PANE_CONTENT_BORDER_KIND, SubstanceConstants.TabContentPaneBorderKind.SINGLE_FULL); - - JFrame.setDefaultLookAndFeelDecorated(true); - JDialog.setDefaultLookAndFeelDecorated(true); - } - - /** - * Loads image from resources - * - * @param name Name of the image - * @return loaded Image - */ - public static BufferedImage loadImage(String name) { - URL imageURL = View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + name + ".png"); - try { - return ImageIO.read(imageURL); - } catch (IOException ex) { - return null; - } - } - - /** - * Sets icon of specified frame to ASDec icon - * - * @param f Frame to set icon in - */ - public static void setWindowIcon(Window f) { - if (Configuration.useRibbonInterface.get()) { - List images = new ArrayList<>(); - MyResizableIcon[] icons = MyRibbonApplicationMenuButtonUI.getIcons(); - MyResizableIcon icon = icons[1]; - int[] sizes = new int[]{256, 128, 64, 42, 40, 32, 20, 16}; - for (int size : sizes) { - icon.setIconSize(size, size); - BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR); - icon.paintIcon(f, bi.getGraphics(), 0, 0); - images.add(bi); - } - f.setIconImages(images); - } else { - List images = new ArrayList<>(); - images.add(loadImage("icon16")); - images.add(loadImage("icon32")); - images.add(loadImage("icon48")); - images.add(loadImage("icon256")); - f.setIconImages(images); - } - } - - /** - * Centers specified frame on the screen - * - * @param f Frame to center on the screen - */ - public static void centerScreen(Window f) { - centerScreen(f, 0); // todo, set screen to the currently active screen instead of the first screen in a multi screen setup, (maybe by using the screen where the main window is now classic or ribbon?) - } - - public static void centerScreen(Window f, int screen) { - - GraphicsDevice[] allDevices = getEnv().getScreenDevices(); - int topLeftX, topLeftY, screenX, screenY, windowPosX, windowPosY; - - if (screen < allDevices.length && screen > -1) { - topLeftX = allDevices[screen].getDefaultConfiguration().getBounds().x; - topLeftY = allDevices[screen].getDefaultConfiguration().getBounds().y; - - screenX = allDevices[screen].getDefaultConfiguration().getBounds().width; - screenY = allDevices[screen].getDefaultConfiguration().getBounds().height; - } else { - topLeftX = allDevices[0].getDefaultConfiguration().getBounds().x; - topLeftY = allDevices[0].getDefaultConfiguration().getBounds().y; - - screenX = allDevices[0].getDefaultConfiguration().getBounds().width; - screenY = allDevices[0].getDefaultConfiguration().getBounds().height; - } - - windowPosX = ((screenX - f.getWidth()) / 2) + topLeftX; - windowPosY = ((screenY - f.getHeight()) / 2) + topLeftY; - - f.setLocation(windowPosX, windowPosY); - } - - public static ImageIcon getIcon(String name, int size) { - ImageIcon icon = getIcon(getPrefferedIconName(name, size)); - if (icon.getIconWidth() == size && icon.getIconHeight() == size) { - return icon; - } - icon.getImage(); - BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); - bi.createGraphics().drawImage(icon.getImage(), 0, 0, size, size, null, null); - return new ImageIcon(bi); - } - - public static ImageIcon getIcon(String name) { - return new ImageIcon(View.class.getClassLoader().getResource("com/jpexs/decompiler/flash/gui/graphics/" + name + ".png")); - } - - private static final KeyStroke escapeStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - - private static final String dispatchWindowClosingActionMapKey = "com.jpexs.dispatch:WINDOW_CLOSING"; - - public static void installEscapeCloseOperation(final JDialog dialog) { - Action dispatchClosing = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent event) { - dialog.dispatchEvent(new WindowEvent( - dialog, WindowEvent.WINDOW_CLOSING)); - } - }; - JRootPane root = dialog.getRootPane(); - root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( - escapeStroke, dispatchWindowClosingActionMapKey); - root.getActionMap().put(dispatchWindowClosingActionMapKey, dispatchClosing); - } - - public static boolean iconExists(String resource) { - return View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png") != null; - } - - private static String getPrefferedIconName(String resource, int preferredSize) { - Matcher m = Pattern.compile("(.*[^0-9])([0-9]+)").matcher(resource); - if (m.matches()) { - int origSize = Integer.parseInt(m.group(2)); - String name = m.group(1); - if (origSize != preferredSize) { - if (iconExists(name + preferredSize)) { - return name + preferredSize; - } - } - } - return resource; - } - - public static ImageWrapperResizableIcon getResizableIcon(String resource, int preferredSize) { - return getResizableIcon(getPrefferedIconName(resource, preferredSize)); - } - - public static ImageWrapperResizableIcon getResizableIcon(String resource) { - return ImageWrapperResizableIcon.getIcon(View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"), new Dimension(256, 256)); - } - - public static MyResizableIcon getMyResizableIcon(String resource) { - try { - return new MyResizableIcon(ImageIO.read(View.class.getResourceAsStream("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"))); - } catch (IOException ex) { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); - return null; - } - } - - public static void execInEventDispatch(Runnable r) { - if (SwingUtilities.isEventDispatchThread()) { - r.run(); - } else { - try { - SwingUtilities.invokeAndWait(r); - } catch (InterruptedException ex) { - } catch (InvocationTargetException ex) { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - - public static void execInEventDispatchLater(Runnable r) { - if (SwingUtilities.isEventDispatchThread()) { - r.run(); - } else { - SwingUtilities.invokeLater(r); - } - } - - public static int showOptionDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageType, final Icon icon, final Object[] options, final Object initialValue) { - final int[] ret = new int[1]; - execInEventDispatch(() -> { - ret[0] = JOptionPane.showOptionDialog(parentComponent, message, title, optionType, messageType, icon, options, initialValue); - }); - return ret[0]; - } - - public static int showConfirmDialog(Component parentComponent, Object message, String title, int optionType) { - return showConfirmDialog(parentComponent, message, title, optionType, JOptionPane.PLAIN_MESSAGE); - } - - public static int showConfirmDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageTyp) { - final int[] ret = new int[1]; - execInEventDispatch(() -> { - ret[0] = JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageTyp); - }); - return ret[0]; - } - - public static int showConfirmDialog(Component parentComponent, String message, String title, int optionType, ConfigurationItem showAgainConfig, int defaultOption) { - return showConfirmDialog(parentComponent, message, title, optionType, JOptionPane.PLAIN_MESSAGE, showAgainConfig, defaultOption); - } - - public static int showConfirmDialog(final Component parentComponent, String message, final String title, final int optionType, final int messageType, ConfigurationItem showAgainConfig, int defaultOption) { - - JCheckBox donotShowAgainCheckBox = null; - JPanel warPanel = null; - if (showAgainConfig != null) { - if (!showAgainConfig.get()) { - return defaultOption; - } - - JLabel warLabel = new JLabel("" + message.replace("\r\n", "
") + ""); - warPanel = new JPanel(new BorderLayout()); - warPanel.add(warLabel, BorderLayout.CENTER); - donotShowAgainCheckBox = new JCheckBox(AppStrings.translate("message.confirm.donotshowagain")); - warPanel.add(donotShowAgainCheckBox, BorderLayout.SOUTH); - } - - final int[] ret = new int[1]; - final Object messageObj = warPanel == null ? message : warPanel; - execInEventDispatch(() -> { - ret[0] = JOptionPane.showConfirmDialog(parentComponent, messageObj, title, optionType, messageType); - }); - - if (donotShowAgainCheckBox != null) { - showAgainConfig.set(!donotShowAgainCheckBox.isSelected()); - } - - return ret[0]; - } - - public static void showMessageDialog(final Component parentComponent, final String message, final String title, final int messageType) { - showMessageDialog(parentComponent, message, title, messageType, null); - } - - public static void showMessageDialog(final Component parentComponent, final String message, final String title, final int messageType, ConfigurationItem showAgainConfig) { - - execInEventDispatch(() -> { - Object msg = message; - JCheckBox donotShowAgainCheckBox = null; - if (showAgainConfig != null) { - if (!showAgainConfig.get()) { - return; - } - - JLabel warLabel = new JLabel("" + message.replace("\r\n", "
") + ""); - final JPanel warPanel = new JPanel(new BorderLayout()); - warPanel.add(warLabel, BorderLayout.CENTER); - donotShowAgainCheckBox = new JCheckBox(AppStrings.translate("message.confirm.donotshowagain")); - warPanel.add(donotShowAgainCheckBox, BorderLayout.SOUTH); - msg = warPanel; - } - final Object fmsg = msg; - - JOptionPane.showMessageDialog(parentComponent, fmsg, title, messageType); - if (donotShowAgainCheckBox != null) { - showAgainConfig.set(!donotShowAgainCheckBox.isSelected()); - } - }); - } - - public static void showMessageDialog(final Component parentComponent, final Object message) { - execInEventDispatch(() -> { - JOptionPane.showMessageDialog(parentComponent, message); - }); - } - - public static String showInputDialog(final Object message, final Object initialSelection) { - final String[] ret = new String[1]; - execInEventDispatch(() -> { - ret[0] = JOptionPane.showInputDialog(message, initialSelection); - }); - return ret[0]; - } - - public static SubstanceColorScheme getColorScheme() { - return SubstanceColorSchemeUtilities.getActiveColorScheme(new JButton(), ComponentState.ENABLED); - } - - public static void refreshTree(JTree tree, TreeModel model) { - List> expandedNodes = getExpandedNodes(tree); - tree.setModel(model); - expandTreeNodes(tree, expandedNodes); - } - - public static List> getExpandedNodes(JTree tree) { - List> expandedNodes = new ArrayList<>(); - int rowCount = tree.getRowCount(); - for (int i = 0; i < rowCount; i++) { - try { - TreePath path = tree.getPathForRow(i); - if (tree.isExpanded(path)) { - List pathAsStringList = new ArrayList<>(); - for (Object pathCompnent : path.getPath()) { - pathAsStringList.add(pathCompnent.toString()); - } - expandedNodes.add(pathAsStringList); - } - } catch (IndexOutOfBoundsException | NullPointerException ex) { - // TreeNode was removed, ignore - } - } - return expandedNodes; - } - - public static void expandTreeNodes(JTree tree, List> pathsToExpand) { - for (List pathAsStringList : pathsToExpand) { - expandTreeNode(tree, pathAsStringList); - } - } - - private static TreePath expandTreeNode(JTree tree, List pathAsStringList) { - TreePath tp = getTreePathByPathStrings(tree, pathAsStringList); - tree.expandPath(tp); - return tp; - } - - public static TreePath getTreePathByPathStrings(JTree tree, List pathAsStringList) { - TreeModel model = tree.getModel(); - if (model == null) { - return null; - } - - Object node = model.getRoot(); - - if (pathAsStringList.isEmpty()) { - return null; - } - if (!pathAsStringList.get(0).equals(node.toString())) { - return null; - } - - List path = new ArrayList<>(); - path.add(node); - - for (int i = 1; i < pathAsStringList.size(); i++) { - String name = pathAsStringList.get(i); - int childCount = model.getChildCount(node); - for (int j = 0; j < childCount; j++) { - Object child = model.getChild(node, j); - if (child.toString().equals(name)) { - node = child; - path.add(node); - break; - } - } - } - - TreePath tp = new TreePath(path.toArray(new Object[path.size()])); - return tp; - } - - public static void expandTreeNodes(JTree tree, TreePath parent, boolean expand) { - expandTreeNodesRecursive(tree, parent, expand); - } - - private static void expandTreeNodesRecursive(JTree tree, TreePath parent, boolean expand) { - TreeModel model = tree.getModel(); - - Object node = parent.getLastPathComponent(); - int childCount = model.getChildCount(node); - for (int j = 0; j < childCount; j++) { - Object child = model.getChild(node, j); - TreePath path = parent.pathByAddingChild(child); - expandTreeNodesRecursive(tree, path, expand); - } - - if (expand) { - tree.expandPath(parent); - } else { - tree.collapsePath(parent); - } - } - - public static void addEditorAction(JEditorPane editor, AbstractAction a, String key, String name, String keyStroke) { - KeyStroke ks = KeyStroke.getKeyStroke(keyStroke); - a.putValue(Action.ACCELERATOR_KEY, ks); - a.putValue(Action.NAME, name); - - String actionName = key; - ActionMap amap = editor.getActionMap(); - InputMap imap = editor.getInputMap(JTextComponent.WHEN_FOCUSED); - imap.put(ks, actionName); - amap.put(actionName, a); - - JPopupMenu pmenu = editor.getComponentPopupMenu(); - JMenuItem findUsagesMenu = new JMenuItem(a); - pmenu.add(findUsagesMenu); - } - - public static boolean navigateUrl(String url) { - if (Desktop.isDesktopSupported()) { - Desktop desktop = Desktop.getDesktop(); - if (desktop.isSupported(Desktop.Action.BROWSE)) { - try { - URI uri = new URI(url); - desktop.browse(uri); - return true; - } catch (URISyntaxException | IOException ex) { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - - return false; - } - - public static JTable autoResizeColWidth(final JTable table, final TableModel model) { - View.execInEventDispatch(() -> { - table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); - table.setModel(model); - - int margin = 5; - - for (int i = 0; i < table.getColumnCount(); i++) { - int vColIndex = i; - DefaultTableColumnModel colModel = (DefaultTableColumnModel) table.getColumnModel(); - TableColumn col = colModel.getColumn(vColIndex); - int width; - - // Get width of column header - TableCellRenderer renderer = col.getHeaderRenderer(); - - if (renderer == null) { - renderer = table.getTableHeader().getDefaultRenderer(); - } - - Component comp = renderer.getTableCellRendererComponent(table, col.getHeaderValue(), false, false, 0, 0); - - width = comp.getPreferredSize().width; - - // Get maximum width of column data - for (int r = 0; r < table.getRowCount(); r++) { - renderer = table.getCellRenderer(r, vColIndex); - comp = renderer.getTableCellRendererComponent(table, table.getValueAt(r, vColIndex), false, false, - r, vColIndex); - width = Math.max(width, comp.getPreferredSize().width); - } - - // Add margin - width += 2 * margin; - - // Set the width - col.setPreferredWidth(width); - } - - ((DefaultTableCellRenderer) table.getTableHeader().getDefaultRenderer()).setHorizontalAlignment( - SwingConstants.LEFT); - - // table.setAutoCreateRowSorter(true); - table.getTableHeader().setReorderingAllowed(false); - }); - - return table; - } - - private static GraphicsEnvironment env; - - public static GraphicsEnvironment getEnv() { - if (env == null) { - env = GraphicsEnvironment.getLocalGraphicsEnvironment(); - } - return env; - } - - private static GraphicsConfiguration conf; - - public static GraphicsConfiguration getDefaultConfiguration() { - if (conf == null) { - conf = getEnv().getDefaultScreenDevice().getDefaultConfiguration(); - } - return conf; - } - - public static BufferedImage toCompatibleImage(BufferedImage image) { - if (image.getColorModel().equals(getDefaultConfiguration().getColorModel())) { - return image; - } - - return getDefaultConfiguration().createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency()); - } - - public static VolatileImage createRenderImage(int width, int height, int transparency) { - VolatileImage image = getDefaultConfiguration().createCompatibleVolatileImage(width, height, transparency); - - int valid = image.validate(getDefaultConfiguration()); - - if (valid == VolatileImage.IMAGE_INCOMPATIBLE) { - image = createRenderImage(width, height, transparency); - return image; - } - - return image; - } -} +/* + * Copyright (C) 2010-2016 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.configuration.Configuration; +import com.jpexs.decompiler.flash.configuration.ConfigurationItem; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.SystemColor; +import java.awt.TexturePaint; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.awt.image.VolatileImage; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.imageio.ImageIO; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ActionMap; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.InputMap; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JRootPane; +import javax.swing.JTable; +import javax.swing.JTree; +import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.plaf.FontUIResource; +import javax.swing.plaf.basic.BasicColorChooserUI; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableModel; +import javax.swing.text.JTextComponent; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; +import org.pushingpixels.flamingo.api.common.icon.ImageWrapperResizableIcon; +import org.pushingpixels.substance.api.ColorSchemeAssociationKind; +import org.pushingpixels.substance.api.ComponentState; +import org.pushingpixels.substance.api.DecorationAreaType; +import org.pushingpixels.substance.api.SubstanceColorScheme; +import org.pushingpixels.substance.api.SubstanceConstants; +import org.pushingpixels.substance.api.SubstanceLookAndFeel; +import org.pushingpixels.substance.api.SubstanceSkin; +import org.pushingpixels.substance.api.fonts.FontPolicy; +import org.pushingpixels.substance.api.fonts.FontSet; +import org.pushingpixels.substance.api.skin.SubstanceOfficeBlue2007LookAndFeel; +import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities; + +/** + * Contains methods for GUI + * + * @author JPEXS + */ +public class View { + + public static Color getDefaultBackgroundColor() { + if (Configuration.useRibbonInterface.get()) { + return SubstanceLookAndFeel.getCurrentSkin().getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.FILL, ComponentState.ENABLED).getBackgroundFillColor(); + } else { + return SystemColor.control; + } + } + + private static Color swfBackgroundColor = null; + + public static void setSwfBackgroundColor(Color swfBackgroundColor) { + View.swfBackgroundColor = swfBackgroundColor; + } + + public static Color getSwfBackgroundColor() { + if (swfBackgroundColor == null) { + return getDefaultBackgroundColor(); + } + return swfBackgroundColor; + } + + private static final BufferedImage transparentTexture; + + public static final TexturePaint transparentPaint; + + private static final Color transparentColor1 = new Color(0x99, 0x99, 0x99); + + private static final Color transparentColor2 = new Color(0x66, 0x66, 0x66); + + static { + transparentTexture = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); + Graphics g = transparentTexture.getGraphics(); + g.setColor(transparentColor1); + g.fillRect(0, 0, 16, 16); + g.setColor(transparentColor2); + g.fillRect(0, 0, 8, 8); + g.fillRect(8, 8, 8, 8); + transparentPaint = new TexturePaint(View.transparentTexture, new Rectangle(0, 0, transparentTexture.getWidth(), transparentTexture.getHeight())); + } + + /** + * Sets windows Look and Feel + */ + public static void setLookAndFeel() { + + //Save default font for Chinese characters + final Font defaultFont = (new JLabel()).getFont(); + try { + + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + + } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) { + } + + try { + LookAndFeel oldLookAndFeel = UIManager.getLookAndFeel(); + if (!(oldLookAndFeel instanceof SubstanceOfficeBlue2007LookAndFeel)) { + UIManager.setLookAndFeel(new SubstanceOfficeBlue2007LookAndFeel()); + oldLookAndFeel.uninitialize(); + } + + SubstanceSkin currentSkin = SubstanceLookAndFeel.getCurrentSkin(); + if (currentSkin != null) { + String currentSkinName = currentSkin.getClass().getName(); + String newSkinName = Configuration.guiSkin.get(); + if (!currentSkinName.equals(newSkinName)) { + SubstanceLookAndFeel.setSkin(newSkinName); + } + } else { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, "Current skin is null"); + SubstanceLookAndFeel.setSkin("com.jpexs.decompiler.flash.gui.OceanicSkin"); + } + + UIManager.put(SubstanceLookAndFeel.COLORIZATION_FACTOR, 0.999);//This works for not changing labels color and not changing Dialogs title + UIManager.put("Tree.expandedIcon", getIcon("expand16")); + UIManager.put("Tree.collapsedIcon", getIcon("collapse16")); + UIManager.put("ColorChooserUI", BasicColorChooserUI.class.getName()); + UIManager.put("ColorChooser.swatchesRecentSwatchSize", new Dimension(20, 20)); + UIManager.put("ColorChooser.swatchesSwatchSize", new Dimension(20, 20)); + UIManager.put("RibbonApplicationMenuPopupPanelUI", MyRibbonApplicationMenuPopupPanelUI.class.getName()); + UIManager.put("RibbonApplicationMenuButtonUI", MyRibbonApplicationMenuButtonUI.class.getName()); + UIManager.put("ProgressBarUI", MyProgressBarUI.class.getName()); + UIManager.put("TextField.background", Color.white); + UIManager.put("FormattedTextField.background", Color.white); + UIManager.put("CommandButtonUI", MyCommandButtonUI.class.getName()); + + FontPolicy pol = SubstanceLookAndFeel.getFontPolicy(); + final FontSet fs = pol.getFontSet("Substance", null); + + //Restore default font for chinese characters + SubstanceLookAndFeel.setFontPolicy(new FontPolicy() { + + private final FontSet fontSet = new FontSet() { + + private FontUIResource controlFont; + + private FontUIResource menuFont; + + private FontUIResource titleFont; + + private FontUIResource windowTitleFont; + + private FontUIResource smallFont; + + private FontUIResource messageFont; + + @Override + public FontUIResource getControlFont() { + if (controlFont == null) { + FontUIResource f = fs.getControlFont(); + controlFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return controlFont; + } + + @Override + public FontUIResource getMenuFont() { + if (menuFont == null) { + FontUIResource f = fs.getMenuFont(); + menuFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return menuFont; + } + + @Override + public FontUIResource getTitleFont() { + if (titleFont == null) { + FontUIResource f = fs.getTitleFont(); + titleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return titleFont; + } + + @Override + public FontUIResource getWindowTitleFont() { + if (windowTitleFont == null) { + FontUIResource f = fs.getWindowTitleFont(); + windowTitleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return windowTitleFont; + } + + @Override + public FontUIResource getSmallFont() { + if (smallFont == null) { + FontUIResource f = fs.getSmallFont(); + smallFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return smallFont; + } + + @Override + public FontUIResource getMessageFont() { + if (messageFont == null) { + FontUIResource f = fs.getMessageFont(); + messageFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return messageFont; + } + }; + + @Override + public FontSet getFontSet(String string, UIDefaults uid) { + return fontSet; + } + }); + } catch (UnsupportedLookAndFeelException ex) { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); + } + + UIManager.put(SubstanceLookAndFeel.TABBED_PANE_CONTENT_BORDER_KIND, SubstanceConstants.TabContentPaneBorderKind.SINGLE_FULL); + + JFrame.setDefaultLookAndFeelDecorated(true); + JDialog.setDefaultLookAndFeelDecorated(true); + } + + /** + * Loads image from resources + * + * @param name Name of the image + * @return loaded Image + */ + public static BufferedImage loadImage(String name) { + URL imageURL = View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + name + ".png"); + try { + return ImageIO.read(imageURL); + } catch (IOException ex) { + return null; + } + } + + /** + * Sets icon of specified frame to ASDec icon + * + * @param f Frame to set icon in + */ + public static void setWindowIcon(Window f) { + if (Configuration.useRibbonInterface.get()) { + List images = new ArrayList<>(); + MyResizableIcon[] icons = MyRibbonApplicationMenuButtonUI.getIcons(); + MyResizableIcon icon = icons[1]; + int[] sizes = new int[]{256, 128, 64, 42, 40, 32, 20, 16}; + for (int size : sizes) { + icon.setIconSize(size, size); + BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR); + icon.paintIcon(f, bi.getGraphics(), 0, 0); + images.add(bi); + } + f.setIconImages(images); + } else { + List images = new ArrayList<>(); + images.add(loadImage("icon16")); + images.add(loadImage("icon32")); + images.add(loadImage("icon48")); + images.add(loadImage("icon256")); + f.setIconImages(images); + } + } + + /** + * Centers specified frame on the screen + * + * @param f Frame to center on the screen + */ + public static void centerScreen(Window f) { + centerScreen(f, 0); // todo, set screen to the currently active screen instead of the first screen in a multi screen setup, (maybe by using the screen where the main window is now classic or ribbon?) + } + + public static void centerScreen(Window f, int screen) { + + GraphicsDevice[] allDevices = getEnv().getScreenDevices(); + int topLeftX, topLeftY, screenX, screenY, windowPosX, windowPosY; + + if (screen < allDevices.length && screen > -1) { + topLeftX = allDevices[screen].getDefaultConfiguration().getBounds().x; + topLeftY = allDevices[screen].getDefaultConfiguration().getBounds().y; + + screenX = allDevices[screen].getDefaultConfiguration().getBounds().width; + screenY = allDevices[screen].getDefaultConfiguration().getBounds().height; + } else { + topLeftX = allDevices[0].getDefaultConfiguration().getBounds().x; + topLeftY = allDevices[0].getDefaultConfiguration().getBounds().y; + + screenX = allDevices[0].getDefaultConfiguration().getBounds().width; + screenY = allDevices[0].getDefaultConfiguration().getBounds().height; + } + + windowPosX = ((screenX - f.getWidth()) / 2) + topLeftX; + windowPosY = ((screenY - f.getHeight()) / 2) + topLeftY; + + f.setLocation(windowPosX, windowPosY); + } + + public static ImageIcon getIcon(String name, int size) { + ImageIcon icon = getIcon(getPrefferedIconName(name, size)); + if (icon.getIconWidth() == size && icon.getIconHeight() == size) { + return icon; + } + icon.getImage(); + BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); + bi.createGraphics().drawImage(icon.getImage(), 0, 0, size, size, null, null); + return new ImageIcon(bi); + } + + public static ImageIcon getIcon(String name) { + return new ImageIcon(View.class.getClassLoader().getResource("com/jpexs/decompiler/flash/gui/graphics/" + name + ".png")); + } + + private static final KeyStroke escapeStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + + private static final String dispatchWindowClosingActionMapKey = "com.jpexs.dispatch:WINDOW_CLOSING"; + + public static void installEscapeCloseOperation(final JDialog dialog) { + Action dispatchClosing = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent event) { + dialog.dispatchEvent(new WindowEvent( + dialog, WindowEvent.WINDOW_CLOSING)); + } + }; + JRootPane root = dialog.getRootPane(); + root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( + escapeStroke, dispatchWindowClosingActionMapKey); + root.getActionMap().put(dispatchWindowClosingActionMapKey, dispatchClosing); + } + + public static boolean iconExists(String resource) { + return View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png") != null; + } + + private static String getPrefferedIconName(String resource, int preferredSize) { + Matcher m = Pattern.compile("(.*[^0-9])([0-9]+)").matcher(resource); + if (m.matches()) { + int origSize = Integer.parseInt(m.group(2)); + String name = m.group(1); + if (origSize != preferredSize) { + if (iconExists(name + preferredSize)) { + return name + preferredSize; + } + } + } + return resource; + } + + public static ImageWrapperResizableIcon getResizableIcon(String resource, int preferredSize) { + return getResizableIcon(getPrefferedIconName(resource, preferredSize)); + } + + public static ImageWrapperResizableIcon getResizableIcon(String resource) { + return ImageWrapperResizableIcon.getIcon(View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"), new Dimension(256, 256)); + } + + public static MyResizableIcon getMyResizableIcon(String resource) { + try { + return new MyResizableIcon(ImageIO.read(View.class.getResourceAsStream("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"))); + } catch (IOException ex) { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); + return null; + } + } + + public static void execInEventDispatch(Runnable r) { + if (SwingUtilities.isEventDispatchThread()) { + r.run(); + } else { + try { + SwingUtilities.invokeAndWait(r); + } catch (InterruptedException ex) { + } catch (InvocationTargetException ex) { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + public static void execInEventDispatchLater(Runnable r) { + if (SwingUtilities.isEventDispatchThread()) { + r.run(); + } else { + SwingUtilities.invokeLater(r); + } + } + + public static int showOptionDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageType, final Icon icon, final Object[] options, final Object initialValue) { + final int[] ret = new int[1]; + execInEventDispatch(() -> { + ret[0] = JOptionPane.showOptionDialog(parentComponent, message, title, optionType, messageType, icon, options, initialValue); + }); + return ret[0]; + } + + public static int showConfirmDialog(Component parentComponent, Object message, String title, int optionType) { + return showConfirmDialog(parentComponent, message, title, optionType, JOptionPane.PLAIN_MESSAGE); + } + + public static int showConfirmDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageTyp) { + final int[] ret = new int[1]; + execInEventDispatch(() -> { + ret[0] = JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageTyp); + }); + return ret[0]; + } + + public static int showConfirmDialog(Component parentComponent, String message, String title, int optionType, ConfigurationItem showAgainConfig, int defaultOption) { + return showConfirmDialog(parentComponent, message, title, optionType, JOptionPane.PLAIN_MESSAGE, showAgainConfig, defaultOption); + } + + public static int showConfirmDialog(final Component parentComponent, String message, final String title, final int optionType, final int messageType, ConfigurationItem showAgainConfig, int defaultOption) { + + JCheckBox donotShowAgainCheckBox = null; + JPanel warPanel = null; + if (showAgainConfig != null) { + if (!showAgainConfig.get()) { + return defaultOption; + } + + JLabel warLabel = new JLabel("" + message.replace("\r\n", "
") + ""); + warPanel = new JPanel(new BorderLayout()); + warPanel.add(warLabel, BorderLayout.CENTER); + donotShowAgainCheckBox = new JCheckBox(AppStrings.translate("message.confirm.donotshowagain")); + warPanel.add(donotShowAgainCheckBox, BorderLayout.SOUTH); + } + + final int[] ret = new int[1]; + final Object messageObj = warPanel == null ? message : warPanel; + execInEventDispatch(() -> { + ret[0] = JOptionPane.showConfirmDialog(parentComponent, messageObj, title, optionType, messageType); + }); + + if (donotShowAgainCheckBox != null) { + showAgainConfig.set(!donotShowAgainCheckBox.isSelected()); + } + + return ret[0]; + } + + public static void showMessageDialog(final Component parentComponent, final String message, final String title, final int messageType) { + showMessageDialog(parentComponent, message, title, messageType, null); + } + + public static void showMessageDialog(final Component parentComponent, final String message, final String title, final int messageType, ConfigurationItem showAgainConfig) { + + execInEventDispatch(() -> { + Object msg = message; + JCheckBox donotShowAgainCheckBox = null; + if (showAgainConfig != null) { + if (!showAgainConfig.get()) { + return; + } + + JLabel warLabel = new JLabel("" + message.replace("\r\n", "
") + ""); + final JPanel warPanel = new JPanel(new BorderLayout()); + warPanel.add(warLabel, BorderLayout.CENTER); + donotShowAgainCheckBox = new JCheckBox(AppStrings.translate("message.confirm.donotshowagain")); + warPanel.add(donotShowAgainCheckBox, BorderLayout.SOUTH); + msg = warPanel; + } + final Object fmsg = msg; + + JOptionPane.showMessageDialog(parentComponent, fmsg, title, messageType); + if (donotShowAgainCheckBox != null) { + showAgainConfig.set(!donotShowAgainCheckBox.isSelected()); + } + }); + } + + public static void showMessageDialog(final Component parentComponent, final Object message) { + execInEventDispatch(() -> { + JOptionPane.showMessageDialog(parentComponent, message); + }); + } + + public static String showInputDialog(final Object message, final Object initialSelection) { + final String[] ret = new String[1]; + execInEventDispatch(() -> { + ret[0] = JOptionPane.showInputDialog(message, initialSelection); + }); + return ret[0]; + } + + public static SubstanceColorScheme getColorScheme() { + return SubstanceColorSchemeUtilities.getActiveColorScheme(new JButton(), ComponentState.ENABLED); + } + + public static void refreshTree(JTree tree, TreeModel model) { + List> expandedNodes = getExpandedNodes(tree); + tree.setModel(model); + expandTreeNodes(tree, expandedNodes); + } + + public static List> getExpandedNodes(JTree tree) { + List> expandedNodes = new ArrayList<>(); + int rowCount = tree.getRowCount(); + for (int i = 0; i < rowCount; i++) { + try { + TreePath path = tree.getPathForRow(i); + if (tree.isExpanded(path)) { + List pathAsStringList = new ArrayList<>(); + for (Object pathCompnent : path.getPath()) { + pathAsStringList.add(pathCompnent.toString()); + } + expandedNodes.add(pathAsStringList); + } + } catch (IndexOutOfBoundsException | NullPointerException ex) { + // TreeNode was removed, ignore + } + } + return expandedNodes; + } + + public static void expandTreeNodes(JTree tree, List> pathsToExpand) { + for (List pathAsStringList : pathsToExpand) { + expandTreeNode(tree, pathAsStringList); + } + } + + private static TreePath expandTreeNode(JTree tree, List pathAsStringList) { + TreePath tp = getTreePathByPathStrings(tree, pathAsStringList); + tree.expandPath(tp); + return tp; + } + + public static TreePath getTreePathByPathStrings(JTree tree, List pathAsStringList) { + TreeModel model = tree.getModel(); + if (model == null) { + return null; + } + + Object node = model.getRoot(); + + if (pathAsStringList.isEmpty()) { + return null; + } + if (!pathAsStringList.get(0).equals(node.toString())) { + return null; + } + + List path = new ArrayList<>(); + path.add(node); + + for (int i = 1; i < pathAsStringList.size(); i++) { + String name = pathAsStringList.get(i); + int childCount = model.getChildCount(node); + for (int j = 0; j < childCount; j++) { + Object child = model.getChild(node, j); + if (child.toString().equals(name)) { + node = child; + path.add(node); + break; + } + } + } + + TreePath tp = new TreePath(path.toArray(new Object[path.size()])); + return tp; + } + + public static void expandTreeNodes(JTree tree, TreePath parent, boolean expand) { + expandTreeNodesRecursive(tree, parent, expand); + } + + private static void expandTreeNodesRecursive(JTree tree, TreePath parent, boolean expand) { + TreeModel model = tree.getModel(); + + Object node = parent.getLastPathComponent(); + int childCount = model.getChildCount(node); + for (int j = 0; j < childCount; j++) { + Object child = model.getChild(node, j); + TreePath path = parent.pathByAddingChild(child); + expandTreeNodesRecursive(tree, path, expand); + } + + if (expand) { + tree.expandPath(parent); + } else { + tree.collapsePath(parent); + } + } + + public static void addEditorAction(JEditorPane editor, AbstractAction a, String key, String name, String keyStroke) { + KeyStroke ks = KeyStroke.getKeyStroke(keyStroke); + a.putValue(Action.ACCELERATOR_KEY, ks); + a.putValue(Action.NAME, name); + + String actionName = key; + ActionMap amap = editor.getActionMap(); + InputMap imap = editor.getInputMap(JTextComponent.WHEN_FOCUSED); + imap.put(ks, actionName); + amap.put(actionName, a); + + JPopupMenu pmenu = editor.getComponentPopupMenu(); + JMenuItem findUsagesMenu = new JMenuItem(a); + pmenu.add(findUsagesMenu); + } + + public static boolean navigateUrl(String url) { + if (Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + if (desktop.isSupported(Desktop.Action.BROWSE)) { + try { + URI uri = new URI(url); + desktop.browse(uri); + return true; + } catch (URISyntaxException | IOException ex) { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + return false; + } + + public static JTable autoResizeColWidth(final JTable table, final TableModel model) { + View.execInEventDispatch(() -> { + table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + table.setModel(model); + + int margin = 5; + + for (int i = 0; i < table.getColumnCount(); i++) { + int vColIndex = i; + DefaultTableColumnModel colModel = (DefaultTableColumnModel) table.getColumnModel(); + TableColumn col = colModel.getColumn(vColIndex); + int width; + + // Get width of column header + TableCellRenderer renderer = col.getHeaderRenderer(); + + if (renderer == null) { + renderer = table.getTableHeader().getDefaultRenderer(); + } + + Component comp = renderer.getTableCellRendererComponent(table, col.getHeaderValue(), false, false, 0, 0); + + width = comp.getPreferredSize().width; + + // Get maximum width of column data + for (int r = 0; r < table.getRowCount(); r++) { + renderer = table.getCellRenderer(r, vColIndex); + comp = renderer.getTableCellRendererComponent(table, table.getValueAt(r, vColIndex), false, false, + r, vColIndex); + width = Math.max(width, comp.getPreferredSize().width); + } + + // Add margin + width += 2 * margin; + + // Set the width + col.setPreferredWidth(width); + } + + ((DefaultTableCellRenderer) table.getTableHeader().getDefaultRenderer()).setHorizontalAlignment( + SwingConstants.LEFT); + + // table.setAutoCreateRowSorter(true); + table.getTableHeader().setReorderingAllowed(false); + }); + + return table; + } + + private static GraphicsEnvironment env; + + public static GraphicsEnvironment getEnv() { + if (env == null) { + env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + } + return env; + } + + private static GraphicsConfiguration conf; + + public static GraphicsConfiguration getDefaultConfiguration() { + if (conf == null) { + conf = getEnv().getDefaultScreenDevice().getDefaultConfiguration(); + } + return conf; + } + + public static BufferedImage toCompatibleImage(BufferedImage image) { + if (image.getColorModel().equals(getDefaultConfiguration().getColorModel())) { + return image; + } + + return getDefaultConfiguration().createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency()); + } + + public static VolatileImage createRenderImage(int width, int height, int transparency) { + VolatileImage image = getDefaultConfiguration().createCompatibleVolatileImage(width, height, transparency); + + int valid = image.validate(getDefaultConfiguration()); + + if (valid == VolatileImage.IMAGE_INCOMPATIBLE) { + image = createRenderImage(width, height, transparency); + return image; + } + + return image; + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java index 9391bbcea..3da2f125d 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java @@ -1,1325 +1,1325 @@ -/* - * Copyright (C) 2010-2016 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.abc; - -import com.jpexs.debugger.flash.Variable; -import com.jpexs.debugger.flash.VariableFlags; -import com.jpexs.debugger.flash.VariableType; -import com.jpexs.debugger.flash.messages.in.InGetVariable; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.ClassPath; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instructions; -import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; -import com.jpexs.decompiler.flash.abc.types.ABCException; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.ValueKind; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; -import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; -import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.TraitMultinameUsage; -import com.jpexs.decompiler.flash.action.parser.ActionParseException; -import com.jpexs.decompiler.flash.action.parser.script.ActionScriptLexer; -import com.jpexs.decompiler.flash.action.parser.script.ParsedSymbol; -import com.jpexs.decompiler.flash.action.parser.script.SymbolType; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.ecma.EcmaScript; -import com.jpexs.decompiler.flash.gui.AppDialog; -import com.jpexs.decompiler.flash.gui.AppStrings; -import com.jpexs.decompiler.flash.gui.DebugPanel; -import com.jpexs.decompiler.flash.gui.DebuggerHandler; -import com.jpexs.decompiler.flash.gui.HeaderLabel; -import com.jpexs.decompiler.flash.gui.Main; -import com.jpexs.decompiler.flash.gui.MainPanel; -import com.jpexs.decompiler.flash.gui.SearchListener; -import com.jpexs.decompiler.flash.gui.SearchPanel; -import com.jpexs.decompiler.flash.gui.TagEditorPanel; -import com.jpexs.decompiler.flash.gui.View; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.DecimalTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.DoubleTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.IntTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.MultinameTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceSetTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.StringTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.UIntTableModel; -import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; -import com.jpexs.decompiler.flash.gui.editor.LinkHandler; -import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.helpers.CancellableWorker; -import com.jpexs.helpers.Helper; -import de.hameister.treetable.MyTreeTable; -import de.hameister.treetable.MyTreeTableModel; -import java.awt.BorderLayout; -import java.awt.Cursor; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import java.io.File; -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -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 java.util.regex.Pattern; -import javax.swing.AbstractAction; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTabbedPane; -import javax.swing.JTable; -import javax.swing.JToggleButton; -import javax.swing.SwingConstants; -import javax.swing.border.BevelBorder; -import javax.swing.event.EventListenerList; -import javax.swing.event.TableModelListener; -import javax.swing.event.TreeModelEvent; -import javax.swing.event.TreeModelListener; -import javax.swing.table.DefaultTableModel; -import javax.swing.text.Highlighter; -import javax.swing.tree.TreePath; -import jsyntaxpane.SyntaxDocument; -import jsyntaxpane.Token; -import jsyntaxpane.TokenType; - -/** - * - * @author JPEXS - */ -public class ABCPanel extends JPanel implements ItemListener, SearchListener, TagEditorPanel { - - private final MainPanel mainPanel; - - public final TraitsList navigator; - - public ABC abc; - - public final DecompiledEditorPane decompiledTextArea; - - public final JScrollPane decompiledScrollPane; - - private final JPersistentSplitPane splitPane; - - private final JTable constantTable; - - public JComboBox constantTypeList; - - public JLabel asmLabel = new HeaderLabel(AppStrings.translate("panel.disassembled")); - - public JLabel decLabel = new HeaderLabel(AppStrings.translate("panel.decompiled")); - - public final DetailPanel detailPanel; - - private final JPanel navPanel; - - public final JTabbedPane tabbedPane; - - public final SearchPanel searchPanel; - - private NewTraitDialog newTraitDialog; - - public final JLabel scriptNameLabel; - - private final DebugPanel debugPanel; - - private final JLabel experimentalLabel = new JLabel(AppStrings.translate("action.edit.experimental")); - - private final JButton editDecompiledButton = new JButton(AppStrings.translate("button.edit"), View.getIcon("edit16")); - - private final JButton saveDecompiledButton = new JButton(AppStrings.translate("button.save"), View.getIcon("save16")); - - private final JButton cancelDecompiledButton = new JButton(AppStrings.translate("button.cancel"), View.getIcon("cancel16")); - - private String lastDecompiled = null; - - public MainPanel getMainPanel() { - return mainPanel; - } - - public List search(final SWF swf, final String txt, boolean ignoreCase, boolean regexp, CancellableWorker worker) { - List ignoredClasses = new ArrayList<>(); - List ignoredNss = new ArrayList<>(); - - if (Configuration._ignoreAdditionalFlexClasses.get()) { - abc.getSwf().getFlexMainClass(ignoredClasses, ignoredNss); - } - if (txt != null && !txt.isEmpty()) { - searchPanel.setOptions(ignoreCase, regexp); - TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); - TreeItem scriptsNode = ttm.getScriptsNode(swf); - final List found = new ArrayList<>(); - if (scriptsNode instanceof ClassesListTreeModel) { - ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode; - List allpacks = clModel.getList(); - final Pattern pat = regexp - ? Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0) - : Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); - int pos = 0; - loop: - for (final ScriptPack pack : allpacks) { - pos++; - if (!pack.isSimple && Configuration.ignoreCLikePackages.get()) { - continue; - } - if (Configuration._ignoreAdditionalFlexClasses.get()) { - String fullName = pack.getClassPath().packageStr.add(pack.getClassPath().className).toRawString(); - if (ignoredClasses.contains(fullName)) { - continue; - } - for (String ns : ignoredNss) { - if (fullName.startsWith(ns + ".")) { - continue loop; - } - } - } - - String workText = AppStrings.translate("work.searching"); - String decAdd = ""; - if (!SWF.isCached(pack)) { - decAdd = ", " + AppStrings.translate("work.decompiling"); - } - - Main.startWork(workText + " \"" + txt + "\"" + decAdd + " - (" + pos + "/" + allpacks.size() + ") " + pack.getClassPath().toString() + "... ", worker); - try { - if (pat.matcher(SWF.getCached(pack).text).find()) { - ABCPanelSearchResult searchResult = new ABCPanelSearchResult(pack); - found.add(searchResult); - } - } catch (InterruptedException ex) { - break; - } - } - } - - return found; - } - - return null; - } - - public void setAbc(ABC abc) { - if (abc == this.abc) { - return; - } - this.abc = abc; - setDecompiledEditMode(false); - navigator.setAbc(abc); - updateConstList(); - } - - public void updateConstList() { - switch (constantTypeList.getSelectedIndex()) { - case 0: - View.autoResizeColWidth(constantTable, new UIntTableModel(abc)); - break; - case 1: - View.autoResizeColWidth(constantTable, new IntTableModel(abc)); - break; - case 2: - View.autoResizeColWidth(constantTable, new DoubleTableModel(abc)); - break; - case 3: - View.autoResizeColWidth(constantTable, new DecimalTableModel(abc)); - break; - case 4: - View.autoResizeColWidth(constantTable, new StringTableModel(abc)); - break; - case 5: - View.autoResizeColWidth(constantTable, new NamespaceTableModel(abc)); - break; - case 6: - View.autoResizeColWidth(constantTable, new NamespaceSetTableModel(abc)); - break; - case 7: - View.autoResizeColWidth(constantTable, new MultinameTableModel(abc)); - break; - } - //DefaultTableColumnModel colModel = (DefaultTableColumnModel) constantTable.getColumnModel(); - //colModel.getColumn(0).setMaxWidth(50); - } - - public SWF getSwf() { - return abc == null ? null : abc.getSwf(); - } - - public List getAbcList() { - SWF swf = getSwf(); - return swf == null ? null : swf.getAbcList(); - } - - public void clearSwf() { - this.abc = null; - constantTable.setModel(new DefaultTableModel()); - navigator.clearAbc(); - decompiledTextArea.clearScript(); - } - - public static class VariableNode { - - public List path = new ArrayList<>(); - - public Long parentId; - - public int level; - - public Variable thisVar; - - public Variable thisTrait; - - public long thisTraitId; - - private List childs; - - private List childTraits; - - @Override - public int hashCode() { - int hash = 3; - hash = 53 * hash + Objects.hashCode(this.parentId); - hash = 53 * hash + (this.thisVar == null ? 0 : Objects.hashCode(this.thisVar.name)); - return hash; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final VariableNode other = (VariableNode) obj; - if (!Objects.equals(this.parentId, other.parentId)) { - return false; - } - if (this.thisVar == null && other.thisVar == null) { - return true; - } - if (this.thisVar == null) { - return false; - } - if (other.thisVar == null) { - return false; - } - return Objects.equals(this.thisVar.name, other.thisVar.name); - } - - public boolean loaded = false; - - private static boolean isTraits(Variable v) { - return (v.vType == VariableType.UNKNOWN && "traits".equals(v.typeName)); - } - - @Override - public String toString() { - if (level == 0) { - return "root"; //TODO: localize? - } - return thisVar.name; - } - - private void refresh() { - if (path.size() > 1) { - path.get(path.size() - 2).reloadChildren(); - } else { - //Main.getDebugHandler().refreshFrame(); - //InFrame fr = Main.getDebugHandler().getFrame(); - } - } - - private void reloadChildren() { - InGetVariable igv = Main.getDebugHandler().getVariable(parentId, thisVar.name, true); - childs = new ArrayList<>(); - childTraits = new ArrayList<>(); - if (thisVar.vType != VariableType.FUNCTION || ((thisVar.flags & VariableFlags.HAS_GETTER) > 0)) { - thisVar = igv.parent; - } - Variable curTrait = null; - - for (int i = 0; i < igv.childs.size(); i++) { - if (!isTraits(igv.childs.get(i))) { - childs.add(igv.childs.get(i)); - childTraits.add(curTrait); - } else { - curTrait = igv.childs.get(i); - } - } - } - - private void ensureLoaded() { - if (!loaded) { - reloadChildren(); - loaded = true; - } - } - - public VariableNode getChildAt(int index) { - ensureLoaded(); - Long parId = 0L; - if (thisVar != null && (thisVar.vType == VariableType.OBJECT || thisVar.vType == VariableType.MOVIECLIP)) { - parId = (Long) thisVar.value; - } - VariableNode vn = new VariableNode(level + 1, childs.get(index), parId, childTraits.get(index)); - vn.path.addAll(path); - vn.path.add(vn); - return vn; - } - - public int getChildCount() { - ensureLoaded(); - return childs.size(); - } - - public VariableNode(int level, Variable thisVar, Long parentId, Variable thisTrait) { - this.parentId = parentId; - this.thisVar = thisVar; - this.level = level; - this.thisTrait = thisTrait; - } - - public VariableNode(int level, Variable thisVar, Long parentId, Variable thisTrait, Long thisTraitId, List vars, List varTraits) { - this.parentId = parentId; - - this.thisVar = thisVar; - - this.level = level; - this.childs = vars; - - this.thisTrait = thisTrait; - - this.childTraits = varTraits; - this.path.add(this); - - loaded = true; - } - } - - public static class VariablesTableModel implements MyTreeTableModel { - - List tableListeners = new ArrayList<>(); - - VariableNode root; - - private final Map> nodeCache = new HashMap<>(); - - protected EventListenerList listenerList = new EventListenerList(); - - private static final int CHANGED = 0; - - private static final int INSERTED = 1; - - private static final int REMOVED = 2; - - private static final int STRUCTURE_CHANGED = 3; - - private final MyTreeTable ttable; - - public VariablesTableModel(MyTreeTable ttable, List vars, List parentIds) { - this.ttable = ttable; - List varTraits = new ArrayList<>(); - for (int i = 0; i < vars.size(); i++) { - varTraits.add(null); - } - root = new VariableNode(0, null, 0L, null, 0L, vars, varTraits); - } - - @Override - public int getColumnCount() { - return 3; - } - - @Override - public String getColumnName(int columnIndex) { - switch (columnIndex) { - case 0: - return AppStrings.translate("variables.column.name"); - case 1: - return AppStrings.translate("variables.column.type"); - case 2: - return AppStrings.translate("variables.column.value"); - default: - return null; - } - } - - @Override - public Class getColumnClass(int columnIndex) { - if (columnIndex == 0) { - return MyTreeTableModel.class; - } - return String.class; - } - - @Override - public Object getValueAt(Object node, int columnIndex) { - if (node == root) { - if (columnIndex == 0) { - return "root"; - } - return ""; - } - Variable v = ((VariableNode) node).thisVar; - - switch (columnIndex) { - case 0: - return v.name; - case 1: - String typeStr = v.getTypeAsStr(); - if ("Object".equals(typeStr)) { - typeStr = v.className; - } - if ("Object".equals(typeStr)) { - typeStr = v.typeName; - } - return typeStr; - case 2: - switch (v.vType) { - case VariableType.OBJECT: - case VariableType.MOVIECLIP: - case VariableType.FUNCTION: - return v.getTypeAsStr() + "(" + v.value + ")"; - case VariableType.STRING: - return "\"" + Helper.escapeActionScriptString("" + v.value) + "\""; - default: - return EcmaScript.toString(v.value); - } - - } - return null; - } - - @Override - public boolean isCellEditable(Object node, int column) { - return column == 0 || (column == 2 && node != root && ((VariableNode) node).thisVar.isPrimitive); - } - - @Override - public void setValueAt(Object aValue, Object node, int column) { - ActionScriptLexer lexer = new ActionScriptLexer(new StringReader("" + aValue)); - ParsedSymbol symb; - try { - symb = lexer.lex(); - ParsedSymbol f = lexer.yylex(); - if (f.type != SymbolType.EOF) { - return; - } - } catch (IOException | ActionParseException ex) { - return; - } - int valType; - switch (symb.type) { - case DOUBLE: - valType = VariableType.NUMBER; - break; - case INTEGER: - valType = VariableType.NUMBER; - break; - case NULL: - valType = VariableType.NULL; - break; - case STRING: - valType = VariableType.STRING; - break; - case UNDEFINED: - valType = VariableType.UNDEFINED; - break; - default: - return; - } - Main.getDebugHandler().setVariable(((VariableNode) node).parentId, ((VariableNode) node).thisVar.name, valType, symb.value); - //((VariableNode) node).refresh(); - Object[] path = new Object[((VariableNode) node).path.size()]; - for (int i = 0; i < path.length; i++) { - path[i] = ((VariableNode) node).path.get(i); - } - valueForPathChanged(new TreePath(path), aValue); - //fireTreeNodesChanged(this, path, new int[0]/*removed*/, new Object[]{node}); - } - - @Override - public Object getRoot() { - return root; - } - - @Override - public Object getChild(Object parent, int index) { - return ((VariableNode) parent).getChildAt(index); - } - - @Override - public int getChildCount(Object parent) { - int cnt = ((VariableNode) parent).getChildCount(); - return cnt; - } - - @Override - public boolean isLeaf(Object node) { - return getChildCount(node) == 0; - } - - @Override - public void valueForPathChanged(TreePath path, Object newValue) { - fireTreeNodesChanged(ttable, path.getParentPath().getPath(), new int[0], new Object[]{path.getLastPathComponent()}); - } - - @Override - public int getIndexOfChild(Object parent, Object child) { - int cnt = getChildCount(parent); - for (int i = 0; i < cnt; i++) { - if (getChild(parent, i) == child) { - return i; - } - } - return -1; - } - - @Override - public void addTreeModelListener(TreeModelListener l) { - listenerList.add(TreeModelListener.class, l); - } - - @Override - public void removeTreeModelListener(TreeModelListener l) { - listenerList.remove(TreeModelListener.class, l); - } - - protected void fireTreeNodesChanged(Object source, Object[] path, int[] childIndices, Object[] children) { - fireTreeNode(CHANGED, source, path, childIndices, children); - } - - protected void fireTreeNodesInserted(Object source, Object[] path, int[] childIndices, Object[] children) { - fireTreeNode(INSERTED, source, path, childIndices, children); - } - - protected void fireTreeNodesRemoved(Object source, Object[] path, int[] childIndices, Object[] children) { - fireTreeNode(REMOVED, source, path, childIndices, children); - } - - protected void fireTreeStructureChanged(Object source, Object[] path, int[] childIndices, Object[] children) { - fireTreeNode(STRUCTURE_CHANGED, source, path, childIndices, children); - } - - private void fireTreeNode(int changeType, Object source, Object[] path, int[] childIndices, Object[] children) { - Object[] listeners = listenerList.getListenerList(); - TreeModelEvent e = new TreeModelEvent(source, path, childIndices, children); - for (int i = listeners.length - 2; i >= 0; i -= 2) { - if (listeners[i] == TreeModelListener.class) { - - switch (changeType) { - case CHANGED: - ((TreeModelListener) listeners[i + 1]).treeNodesChanged(e); - break; - case INSERTED: - ((TreeModelListener) listeners[i + 1]).treeNodesInserted(e); - break; - case REMOVED: - ((TreeModelListener) listeners[i + 1]).treeNodesRemoved(e); - break; - case STRUCTURE_CHANGED: - ((TreeModelListener) listeners[i + 1]).treeStructureChanged(e); - break; - default: - break; - } - - } - } - } - } - - public ABCPanel(MainPanel mainPanel) { - - this.mainPanel = mainPanel; - setLayout(new BorderLayout()); - - decompiledTextArea = new DecompiledEditorPane(this); - decompiledTextArea.addTextChangedListener(this::decompiledTextAreaTextChanged); - - decompiledTextArea.setLinkHandler(new LinkHandler() { - - @Override - public boolean isLink(Token token) { - return hasDeclaration(token.start); - } - - @Override - public void handleLink(Token token) { - gotoDeclaration(token.start); - } - - @Override - public Highlighter.HighlightPainter linkPainter() { - return decompiledTextArea.linkPainter(); - } - }); - - searchPanel = new SearchPanel<>(new FlowLayout(), this); - - decompiledScrollPane = new JScrollPane(decompiledTextArea); - - JPanel iconDecPanel = new JPanel(); - iconDecPanel.setLayout(new BoxLayout(iconDecPanel, BoxLayout.Y_AXIS)); - JPanel iconsPanel = new JPanel(); - iconsPanel.setLayout(new BoxLayout(iconsPanel, BoxLayout.X_AXIS)); - - JButton newTraitButton = new JButton(View.getIcon("traitadd16")); - newTraitButton.setMargin(new Insets(5, 5, 5, 5)); - newTraitButton.addActionListener(this::addTraitButtonActionPerformed); - newTraitButton.setToolTipText(AppStrings.translate("button.addtrait")); - iconsPanel.add(newTraitButton); - - scriptNameLabel = new JLabel("-"); - scriptNameLabel.setAlignmentX(0); - iconsPanel.setAlignmentX(0); - decompiledScrollPane.setAlignmentX(0); - iconDecPanel.add(scriptNameLabel); - iconDecPanel.add(iconsPanel); - iconDecPanel.add(decompiledScrollPane); - - final JPanel decButtonsPan = new JPanel(new FlowLayout()); - decButtonsPan.setBorder(new BevelBorder(BevelBorder.RAISED)); - decButtonsPan.add(editDecompiledButton); - decButtonsPan.add(experimentalLabel); - decButtonsPan.add(saveDecompiledButton); - decButtonsPan.add(cancelDecompiledButton); - - editDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); - saveDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); - cancelDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); - - saveDecompiledButton.addActionListener(this::saveDecompiledButtonActionPerformed); - editDecompiledButton.addActionListener(this::editDecompiledButtonActionPerformed); - cancelDecompiledButton.addActionListener(this::cancelDecompiledButtonActionPerformed); - - saveDecompiledButton.setVisible(false); - cancelDecompiledButton.setVisible(false); - decButtonsPan.setAlignmentX(0); - - JPanel decPanel = new JPanel(new BorderLayout()); - decPanel.add(searchPanel, BorderLayout.NORTH); - decPanel.add(iconDecPanel, BorderLayout.CENTER); - decPanel.add(decButtonsPan, BorderLayout.SOUTH); - detailPanel = new DetailPanel(this); - JPanel panB = new JPanel(); - panB.setLayout(new BorderLayout()); - panB.add(decLabel, BorderLayout.NORTH); - - Main.getDebugHandler().addConnectionListener(new DebuggerHandler.ConnectionListener() { - - @Override - public void connected() { - decButtonsPan.setVisible(false); - } - - @Override - public void disconnected() { - decButtonsPan.setVisible(true); - } - }); - - debugPanel = new DebugPanel(); - - JPersistentSplitPane sp2; - - panB.add(sp2 = new JPersistentSplitPane(JSplitPane.VERTICAL_SPLIT, decPanel, debugPanel, Configuration.guiAvm2VarsSplitPaneDividerLocationPercent), BorderLayout.CENTER); - sp2.setContinuousLayout(true); - - debugPanel.setVisible(false); - - decLabel.setHorizontalAlignment(SwingConstants.CENTER); - //decLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - splitPane = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, panB, detailPanel, Configuration.guiAvm2SplitPaneDividerLocationPercent); - splitPane.setContinuousLayout(true); - - decompiledTextArea.changeContentType("text/actionscript"); - decompiledTextArea.setFont(new Font("Monospaced", Font.PLAIN, decompiledTextArea.getFont().getSize())); - - View.addEditorAction(decompiledTextArea, new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - int multinameIndex = decompiledTextArea.getMultinameUnderCaret(); - if (multinameIndex > -1) { - UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, ABCPanel.this, false); - usageFrame.setVisible(true); - } - } - }, "find-usages", AppStrings.translate("abc.action.find-usages"), "control U"); - - View.addEditorAction(decompiledTextArea, new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - gotoDeclaration(decompiledTextArea.getCaretPosition()); - } - }, "find-declaration", AppStrings.translate("abc.action.find-declaration"), "control B"); - - CtrlClickHandler cch = new CtrlClickHandler(); - decompiledTextArea.addKeyListener(cch); - decompiledTextArea.addMouseListener(cch); - decompiledTextArea.addMouseMotionListener(cch); - - navigator = new TraitsList(this); - - navPanel = new JPanel(new BorderLayout()); - JPanel navIconsPanel = new JPanel(); - navIconsPanel.setLayout(new BoxLayout(navIconsPanel, BoxLayout.X_AXIS)); - final JToggleButton sortButton = new JToggleButton(View.getIcon("sort16")); - sortButton.setMargin(new Insets(3, 3, 3, 3)); - navIconsPanel.add(sortButton); - navPanel.add(navIconsPanel, BorderLayout.SOUTH); - navPanel.add(new JScrollPane(navigator), BorderLayout.CENTER); - sortButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - navigator.setSorted(sortButton.isSelected()); - navigator.updateUI(); - } - }); - - tabbedPane = new JTabbedPane(); - tabbedPane.addTab(AppStrings.translate("traits"), navPanel); - add(splitPane, BorderLayout.CENTER); - - JPanel panConstants = new JPanel(); - panConstants.setLayout(new BorderLayout()); - - constantTypeList = new JComboBox<>(new String[]{"UINT", "INT", "DOUBLE", "DECIMAL", "STRING", "NAMESPACE", "NAMESPACESET", "MULTINAME"}); - constantTable = new JTable(); - if (abc != null) { - View.autoResizeColWidth(constantTable, new UIntTableModel(abc)); - } - constantTable.setAutoCreateRowSorter(true); - - final ABCPanel t = this; - constantTable.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (e.getClickCount() == 2) { - if (constantTypeList.getSelectedIndex() == 7) { //MULTINAME - int rowIndex = constantTable.getSelectedRow(); - if (rowIndex == -1) { - return; - } - int multinameIndex = constantTable.convertRowIndexToModel(rowIndex); - if (multinameIndex > 0) { - UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, t, false); - usageFrame.setVisible(true); - } - } - } - } - }); - constantTypeList.addItemListener(this); - panConstants.add(constantTypeList, BorderLayout.NORTH); - panConstants.add(new JScrollPane(constantTable), BorderLayout.CENTER); - tabbedPane.addTab(AppStrings.translate("constants"), panConstants); - } - - private void decompiledTextAreaTextChanged() { - setModified(true); - } - - private boolean isModified() { - return saveDecompiledButton.isVisible() && saveDecompiledButton.isEnabled(); - } - - private void setModified(boolean value) { - saveDecompiledButton.setEnabled(value); - cancelDecompiledButton.setEnabled(value); - } - - private boolean hasDeclaration(int pos) { - if (decompiledTextArea == null) { - return false; //? - } - SyntaxDocument sd = (SyntaxDocument) decompiledTextArea.getDocument(); - Token t = sd.getTokenAt(pos + 1); - if (t == null || (t.type != TokenType.IDENTIFIER && t.type != TokenType.KEYWORD && t.type != TokenType.REGEX)) { - return false; - } - Reference abcIndex = new Reference<>(0); - Reference classIndex = new Reference<>(0); - Reference traitIndex = new Reference<>(0); - Reference multinameIndexRef = new Reference<>(0); - Reference classTrait = new Reference<>(false); - - if (decompiledTextArea.getPropertyTypeAtPos(pos, abcIndex, classIndex, traitIndex, classTrait, multinameIndexRef)) { - return true; - } - int multinameIndex = decompiledTextArea.getMultinameAtPos(pos); - if (multinameIndex > -1) { - if (multinameIndex == 0) { - return false; - } - List usages = abc.findMultinameDefinition(multinameIndex); - - Multiname m = abc.constants.getMultiname(multinameIndex); - - //search other ABC tags if this is not private multiname - if (m.getSingleNamespaceIndex(abc.constants) > 0 && abc.constants.getNamespace(m.getSingleNamespaceIndex(abc.constants)).kind != Namespace.KIND_PRIVATE) { - for (ABCContainerTag at : getAbcList()) { - ABC a = at.getABC(); - if (a == abc) { - continue; - } - - int mid = a.constants.getMultinameId(m, abc.constants); - if (mid > 0) { - usages.addAll(a.findMultinameDefinition(mid)); - } - } - } - - //more than one? display list - if (!usages.isEmpty()) { - return true; - } - } - - return decompiledTextArea.getLocalDeclarationOfPos(pos, new Reference<>(null)) != -1; - } - - private void gotoDeclaration(int pos) { - Reference abcIndex = new Reference<>(0); - Reference classIndex = new Reference<>(0); - Reference traitIndex = new Reference<>(0); - Reference classTrait = new Reference<>(false); - Reference multinameIndexRef = new Reference<>(0); - - if (decompiledTextArea.getPropertyTypeAtPos(pos, abcIndex, classIndex, traitIndex, classTrait, multinameIndexRef)) { - UsageFrame.gotoUsage(ABCPanel.this, new TraitMultinameUsage(getAbcList().get(abcIndex.getVal()).getABC(), multinameIndexRef.getVal(), classIndex.getVal(), traitIndex.getVal(), classTrait.getVal(), null, -1) { - }); - return; - } - int multinameIndex = decompiledTextArea.getMultinameAtPos(pos); - if (multinameIndex > -1) { - List usages = abc.findMultinameDefinition(multinameIndex); - - Multiname m = abc.constants.getMultiname(multinameIndex); - //search other ABC tags if this is not private multiname - if (m.getSingleNamespaceIndex(abc.constants) > 0 && m.getSingleNamespace(abc.constants).kind != Namespace.KIND_PRIVATE) { - for (ABCContainerTag at : getAbcList()) { - ABC a = at.getABC(); - if (a == abc) { - continue; - } - int mid = a.constants.getMultinameId(m, abc.constants); - if (mid > 0) { - usages.addAll(a.findMultinameDefinition(mid)); - } - } - } - - //more than one? display list - if (usages.size() > 1) { - UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, ABCPanel.this, true); - usageFrame.setVisible(true); - return; - } else if (!usages.isEmpty()) { //one - UsageFrame.gotoUsage(ABCPanel.this, usages.get(0)); - return; - } - } - - int dpos = decompiledTextArea.getLocalDeclarationOfPos(pos, new Reference<>(null)); - if (dpos > -1) { - decompiledTextArea.setCaretPosition(dpos); - - } - - } - - private class CtrlClickHandler extends KeyAdapter implements MouseListener, MouseMotionListener { - - private boolean ctrlDown = false; - - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == 17 && !decompiledTextArea.isEditable()) { - ctrlDown = true; - //decompiledTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } - } - - @Override - public void keyReleased(KeyEvent e) { - if (e.getKeyCode() == 17) { - ctrlDown = false; - //decompiledTextArea.setCursor(Cursor.getDefaultCursor()); - } - } - - @Override - public void mouseClicked(MouseEvent e) { - if (ctrlDown && e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 1 && !decompiledTextArea.isEditable()) { - ctrlDown = false; - //decompiledTextArea.setCursor(Cursor.getDefaultCursor()); - //gotoDeclaration(); - } - } - - @Override - public void mousePressed(MouseEvent e) { - - } - - @Override - public void mouseReleased(MouseEvent e) { - } - - @Override - public void mouseEntered(MouseEvent e) { - } - - @Override - public void mouseExited(MouseEvent e) { - ctrlDown = false; - decompiledTextArea.setCursor(Cursor.getDefaultCursor()); - } - - @Override - public void mouseDragged(MouseEvent e) { - } - - @Override - public void mouseMoved(MouseEvent e) { - if (ctrlDown && decompiledTextArea.isEditable()) { - ctrlDown = false; - decompiledTextArea.setCursor(Cursor.getDefaultCursor()); - } - } - } - - public void reload() { - lastDecompiled = ""; - SWF swf = getSwf(); - if (swf != null) { - swf.clearScriptCache(); - } - - decompiledTextArea.reloadClass(); - detailPanel.methodTraitPanel.methodCodePanel.clear(); - } - - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getSource() == constantTypeList) { - int index = ((JComboBox) e.getSource()).getSelectedIndex(); - if (index == -1) { - return; - } - updateConstList(); - } - } - - public void display() { - setVisible(true); - } - - public void hilightScript(SWF swf, String name) { - TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); - TreeItem scriptsNode = ttm.getScriptsNode(swf); - if (scriptsNode instanceof ClassesListTreeModel) { - ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode; - ScriptPack pack = null; - for (ScriptPack item : clModel.getList()) { - if (!item.isSimple && Configuration.ignoreCLikePackages.get()) { - continue; - } - ClassPath classPath = item.getClassPath(); - - // first check the className to avoid calling unnecessary toString - if (name.endsWith(classPath.className) && classPath.toRawString().equals(name)) { - pack = item; - break; - } - } - if (pack != null) { - hilightScript(pack); - } - } - } - - public void hilightScript(ScriptPack pack) { - TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); - TreePath tp0 = ttm.getTreePath(pack); - if (tp0 == null) { - mainPanel.closeTagTreeSearch(); - tp0 = ttm.getTreePath(pack); - } - final TreePath tp = tp0; - View.execInEventDispatchLater(() -> { - mainPanel.tagTree.setSelectionPath(tp); - mainPanel.tagTree.scrollPathToVisible(tp); - }); - - } - - @Override - public void updateSearchPos(ABCPanelSearchResult item) { - ScriptPack pack = item.getScriptPack(); - setAbc(pack.abc); - decompiledTextArea.setScript(pack, false); - hilightScript(pack); - decompiledTextArea.setCaretPosition(0); - - View.execInEventDispatchLater(() -> { - searchPanel.showQuickFindDialog(decompiledTextArea); - }); - - } - - public boolean isDirectEditing() { - return saveDecompiledButton.isVisible() && saveDecompiledButton.isEnabled(); - } - - public void setDecompiledEditMode(boolean val) { - View.execInEventDispatch(new Runnable() { - - @Override - public void run() { - if (val) { - lastDecompiled = decompiledTextArea.getText(); - } else { - decompiledTextArea.setText(lastDecompiled); - } - - decompiledTextArea.setEditable(val); - saveDecompiledButton.setVisible(val); - saveDecompiledButton.setEnabled(false); - editDecompiledButton.setVisible(!val); - experimentalLabel.setVisible(!val); - cancelDecompiledButton.setVisible(val); - decompiledTextArea.getCaret().setVisible(true); - decLabel.setIcon(val ? View.getIcon("editing16") : null); - detailPanel.setVisible(!val); - - decompiledTextArea.ignoreCarret = val; - decompiledTextArea.requestFocusInWindow(); - } - }); - - } - - private void editDecompiledButtonActionPerformed(ActionEvent evt) { - File swc = Configuration.getPlayerSWC(); - if (swc == null || !swc.exists()) { - if (View.showConfirmDialog(this, AppStrings.translate("message.playerpath.lib.notset"), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE) == JOptionPane.OK_OPTION) { - Main.advancedSettings("paths"); - return; - } - } - if (View.showConfirmDialog(null, AppStrings.translate("message.confirm.experimental.function"), AppStrings.translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, Configuration.warningExperimentalAS3Edit, JOptionPane.OK_OPTION) == JOptionPane.OK_OPTION) { - setDecompiledEditMode(true); - } - } - - private void cancelDecompiledButtonActionPerformed(ActionEvent evt) { - setDecompiledEditMode(false); - } - - private void saveDecompiledButtonActionPerformed(ActionEvent evt) { - ScriptPack pack = decompiledTextArea.getScriptLeaf(); - int oldIndex = pack.scriptIndex; - SWF.uncache(pack); - - try { - String oldSp = pack.getClassPath().toRawString(); - /*List packs = abc.script_info.get(oldIndex).getPacks(abc, oldIndex, null, pack.allABCs); - if (!packs.isEmpty()) { - - }*/ - - String as = decompiledTextArea.getText(); - abc.replaceScriptPack(pack, as); - lastDecompiled = as; - setDecompiledEditMode(false); - mainPanel.updateClassesList(); - - if (oldSp != null) { - hilightScript(getSwf(), oldSp); - } - - reload(); - View.showMessageDialog(this, AppStrings.translate("message.action.saved"), AppStrings.translate("dialog.message.title"), JOptionPane.INFORMATION_MESSAGE, Configuration.showCodeSavedMessage); - } catch (AVM2ParseException ex) { - abc.script_info.get(oldIndex).delete(abc, false); - decompiledTextArea.gotoLine((int) ex.line); - decompiledTextArea.markError(); - View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } catch (CompilationException ex) { - abc.script_info.get(oldIndex).delete(abc, false); - decompiledTextArea.gotoLine((int) ex.line); - decompiledTextArea.markError(); - View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - - } catch (Throwable ex) { - Logger.getLogger(ABCPanel.class - .getName()).log(Level.SEVERE, null, ex); - } - } - - private void addTraitButtonActionPerformed(ActionEvent evt) { - int class_index = decompiledTextArea.getClassIndex(); - if (class_index < 0) { - return; - } - if (newTraitDialog == null) { - newTraitDialog = new NewTraitDialog(); - } - int void_type = abc.constants.getPublicQnameId("void", true);//abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("void"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); - int int_type = abc.constants.getPublicQnameId("int", true); //abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("int"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); - - Trait t = null; - int kind; - int nskind; - String name = null; - boolean isStatic; - Multiname m; - - boolean again = false; - loopm: - do { - if (again) { - View.showMessageDialog(null, AppStrings.translate("error.trait.exists").replace("%name%", name), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - again = false; - if (newTraitDialog.showDialog() != AppDialog.OK_OPTION) { - return; - } - kind = newTraitDialog.getTraitType(); - nskind = newTraitDialog.getNamespaceKind(); - name = newTraitDialog.getTraitName(); - isStatic = newTraitDialog.getStatic(); - m = Multiname.createQName(false, abc.constants.getStringId(name, true), abc.constants.getNamespaceId(nskind, "", 0, true)); - int mid = abc.constants.getMultinameId(m, false); - if (mid <= 0) { - break; - } - for (Trait tr : abc.class_info.get(class_index).static_traits.traits) { - if (tr.name_index == mid) { - again = true; - break; - } - } - - for (Trait tr : abc.instance_info.get(class_index).instance_traits.traits) { - if (tr.name_index == mid) { - again = true; - break; - } - } - } while (again); - switch (kind) { - case Trait.TRAIT_GETTER: - case Trait.TRAIT_SETTER: - case Trait.TRAIT_METHOD: - TraitMethodGetterSetter tm = new TraitMethodGetterSetter(); - MethodInfo mi = new MethodInfo(new int[0], void_type, abc.constants.getStringId(name, true), 0, new ValueKind[0], new int[0]); - int method_info = abc.addMethodInfo(mi); - tm.method_info = method_info; - MethodBody body = new MethodBody(abc, new Traits(), new byte[0], new ABCException[0]); - body.method_info = method_info; - body.init_scope_depth = 1; - body.max_regs = 1; - body.max_scope_depth = 1; - body.max_stack = 1; - body.exceptions = new ABCException[0]; - AVM2Code code = new AVM2Code(); - code.code.add(new AVM2Instruction(0, AVM2Instructions.GetLocal0, null)); - code.code.add(new AVM2Instruction(0, AVM2Instructions.PushScope, null)); - code.code.add(new AVM2Instruction(0, AVM2Instructions.ReturnVoid, null)); - body.setCode(code); - Traits traits = new Traits(); - traits.traits = new ArrayList<>(); - body.traits = traits; - abc.addMethodBody(body); - t = tm; - break; - case Trait.TRAIT_SLOT: - case Trait.TRAIT_CONST: - TraitSlotConst ts = new TraitSlotConst(); - ts.type_index = int_type; - ts.value_kind = ValueKind.CONSTANT_Int; - ts.value_index = abc.constants.getIntId(0, true); - t = ts; - break; - } - if (t != null) { - t.kindType = kind; - t.name_index = abc.constants.getMultinameId(m, true); - int traitId; - if (isStatic) { - traitId = abc.class_info.get(class_index).static_traits.addTrait(t); - } else { - traitId = abc.class_info.get(class_index).static_traits.traits.size() + abc.instance_info.get(class_index).instance_traits.addTrait(t); - } - int scriptIndex = decompiledTextArea.getScriptLeaf().scriptIndex; - if (scriptIndex >= 0 && scriptIndex < abc.script_info.size()) { - abc.script_info.get(scriptIndex).setModified(true); - } - ((Tag) abc.parentTag).setModified(true); - reload(); - decompiledTextArea.gotoTrait(traitId); - } - } - - @Override - public boolean tryAutoSave() { - // todo: implement - return false; - } - - @Override - public boolean isEditing() { - return detailPanel.isEditing() || isModified(); - } -} +/* + * Copyright (C) 2010-2016 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.abc; + +import com.jpexs.debugger.flash.Variable; +import com.jpexs.debugger.flash.VariableFlags; +import com.jpexs.debugger.flash.VariableType; +import com.jpexs.debugger.flash.messages.in.InGetVariable; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ClassPath; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instructions; +import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.ValueKind; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.TraitMultinameUsage; +import com.jpexs.decompiler.flash.action.parser.ActionParseException; +import com.jpexs.decompiler.flash.action.parser.script.ActionScriptLexer; +import com.jpexs.decompiler.flash.action.parser.script.ParsedSymbol; +import com.jpexs.decompiler.flash.action.parser.script.SymbolType; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.flash.gui.AppDialog; +import com.jpexs.decompiler.flash.gui.AppStrings; +import com.jpexs.decompiler.flash.gui.DebugPanel; +import com.jpexs.decompiler.flash.gui.DebuggerHandler; +import com.jpexs.decompiler.flash.gui.HeaderLabel; +import com.jpexs.decompiler.flash.gui.Main; +import com.jpexs.decompiler.flash.gui.MainPanel; +import com.jpexs.decompiler.flash.gui.SearchListener; +import com.jpexs.decompiler.flash.gui.SearchPanel; +import com.jpexs.decompiler.flash.gui.TagEditorPanel; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.DecimalTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.DoubleTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.IntTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.MultinameTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceSetTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.StringTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.UIntTableModel; +import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; +import com.jpexs.decompiler.flash.gui.editor.LinkHandler; +import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.helpers.CancellableWorker; +import com.jpexs.helpers.Helper; +import de.hameister.treetable.MyTreeTable; +import de.hameister.treetable.MyTreeTableModel; +import java.awt.BorderLayout; +import java.awt.Cursor; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +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 java.util.regex.Pattern; +import javax.swing.AbstractAction; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTable; +import javax.swing.JToggleButton; +import javax.swing.SwingConstants; +import javax.swing.border.BevelBorder; +import javax.swing.event.EventListenerList; +import javax.swing.event.TableModelListener; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; +import javax.swing.table.DefaultTableModel; +import javax.swing.text.Highlighter; +import javax.swing.tree.TreePath; +import jsyntaxpane.SyntaxDocument; +import jsyntaxpane.Token; +import jsyntaxpane.TokenType; + +/** + * + * @author JPEXS + */ +public class ABCPanel extends JPanel implements ItemListener, SearchListener, TagEditorPanel { + + private final MainPanel mainPanel; + + public final TraitsList navigator; + + public ABC abc; + + public final DecompiledEditorPane decompiledTextArea; + + public final JScrollPane decompiledScrollPane; + + private final JPersistentSplitPane splitPane; + + private final JTable constantTable; + + public JComboBox constantTypeList; + + public JLabel asmLabel = new HeaderLabel(AppStrings.translate("panel.disassembled")); + + public JLabel decLabel = new HeaderLabel(AppStrings.translate("panel.decompiled")); + + public final DetailPanel detailPanel; + + private final JPanel navPanel; + + public final JTabbedPane tabbedPane; + + public final SearchPanel searchPanel; + + private NewTraitDialog newTraitDialog; + + public final JLabel scriptNameLabel; + + private final DebugPanel debugPanel; + + private final JLabel experimentalLabel = new JLabel(AppStrings.translate("action.edit.experimental")); + + private final JButton editDecompiledButton = new JButton(AppStrings.translate("button.edit"), View.getIcon("edit16")); + + private final JButton saveDecompiledButton = new JButton(AppStrings.translate("button.save"), View.getIcon("save16")); + + private final JButton cancelDecompiledButton = new JButton(AppStrings.translate("button.cancel"), View.getIcon("cancel16")); + + private String lastDecompiled = null; + + public MainPanel getMainPanel() { + return mainPanel; + } + + public List search(final SWF swf, final String txt, boolean ignoreCase, boolean regexp, CancellableWorker worker) { + List ignoredClasses = new ArrayList<>(); + List ignoredNss = new ArrayList<>(); + + if (Configuration._ignoreAdditionalFlexClasses.get()) { + abc.getSwf().getFlexMainClass(ignoredClasses, ignoredNss); + } + if (txt != null && !txt.isEmpty()) { + searchPanel.setOptions(ignoreCase, regexp); + TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); + TreeItem scriptsNode = ttm.getScriptsNode(swf); + final List found = new ArrayList<>(); + if (scriptsNode instanceof ClassesListTreeModel) { + ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode; + List allpacks = clModel.getList(); + final Pattern pat = regexp + ? Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0) + : Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); + int pos = 0; + loop: + for (final ScriptPack pack : allpacks) { + pos++; + if (!pack.isSimple && Configuration.ignoreCLikePackages.get()) { + continue; + } + if (Configuration._ignoreAdditionalFlexClasses.get()) { + String fullName = pack.getClassPath().packageStr.add(pack.getClassPath().className).toRawString(); + if (ignoredClasses.contains(fullName)) { + continue; + } + for (String ns : ignoredNss) { + if (fullName.startsWith(ns + ".")) { + continue loop; + } + } + } + + String workText = AppStrings.translate("work.searching"); + String decAdd = ""; + if (!SWF.isCached(pack)) { + decAdd = ", " + AppStrings.translate("work.decompiling"); + } + + Main.startWork(workText + " \"" + txt + "\"" + decAdd + " - (" + pos + "/" + allpacks.size() + ") " + pack.getClassPath().toString() + "... ", worker); + try { + if (pat.matcher(SWF.getCached(pack).text).find()) { + ABCPanelSearchResult searchResult = new ABCPanelSearchResult(pack); + found.add(searchResult); + } + } catch (InterruptedException ex) { + break; + } + } + } + + return found; + } + + return null; + } + + public void setAbc(ABC abc) { + if (abc == this.abc) { + return; + } + this.abc = abc; + setDecompiledEditMode(false); + navigator.setAbc(abc); + updateConstList(); + } + + public void updateConstList() { + switch (constantTypeList.getSelectedIndex()) { + case 0: + View.autoResizeColWidth(constantTable, new UIntTableModel(abc)); + break; + case 1: + View.autoResizeColWidth(constantTable, new IntTableModel(abc)); + break; + case 2: + View.autoResizeColWidth(constantTable, new DoubleTableModel(abc)); + break; + case 3: + View.autoResizeColWidth(constantTable, new DecimalTableModel(abc)); + break; + case 4: + View.autoResizeColWidth(constantTable, new StringTableModel(abc)); + break; + case 5: + View.autoResizeColWidth(constantTable, new NamespaceTableModel(abc)); + break; + case 6: + View.autoResizeColWidth(constantTable, new NamespaceSetTableModel(abc)); + break; + case 7: + View.autoResizeColWidth(constantTable, new MultinameTableModel(abc)); + break; + } + //DefaultTableColumnModel colModel = (DefaultTableColumnModel) constantTable.getColumnModel(); + //colModel.getColumn(0).setMaxWidth(50); + } + + public SWF getSwf() { + return abc == null ? null : abc.getSwf(); + } + + public List getAbcList() { + SWF swf = getSwf(); + return swf == null ? null : swf.getAbcList(); + } + + public void clearSwf() { + this.abc = null; + constantTable.setModel(new DefaultTableModel()); + navigator.clearAbc(); + decompiledTextArea.clearScript(); + } + + public static class VariableNode { + + public List path = new ArrayList<>(); + + public Long parentId; + + public int level; + + public Variable thisVar; + + public Variable thisTrait; + + public long thisTraitId; + + private List childs; + + private List childTraits; + + @Override + public int hashCode() { + int hash = 3; + hash = 53 * hash + Objects.hashCode(this.parentId); + hash = 53 * hash + (this.thisVar == null ? 0 : Objects.hashCode(this.thisVar.name)); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final VariableNode other = (VariableNode) obj; + if (!Objects.equals(this.parentId, other.parentId)) { + return false; + } + if (this.thisVar == null && other.thisVar == null) { + return true; + } + if (this.thisVar == null) { + return false; + } + if (other.thisVar == null) { + return false; + } + return Objects.equals(this.thisVar.name, other.thisVar.name); + } + + public boolean loaded = false; + + private static boolean isTraits(Variable v) { + return (v.vType == VariableType.UNKNOWN && "traits".equals(v.typeName)); + } + + @Override + public String toString() { + if (level == 0) { + return "root"; //TODO: localize? + } + return thisVar.name; + } + + private void refresh() { + if (path.size() > 1) { + path.get(path.size() - 2).reloadChildren(); + } else { + //Main.getDebugHandler().refreshFrame(); + //InFrame fr = Main.getDebugHandler().getFrame(); + } + } + + private void reloadChildren() { + InGetVariable igv = Main.getDebugHandler().getVariable(parentId, thisVar.name, true); + childs = new ArrayList<>(); + childTraits = new ArrayList<>(); + if (thisVar.vType != VariableType.FUNCTION || ((thisVar.flags & VariableFlags.HAS_GETTER) > 0)) { + thisVar = igv.parent; + } + Variable curTrait = null; + + for (int i = 0; i < igv.childs.size(); i++) { + if (!isTraits(igv.childs.get(i))) { + childs.add(igv.childs.get(i)); + childTraits.add(curTrait); + } else { + curTrait = igv.childs.get(i); + } + } + } + + private void ensureLoaded() { + if (!loaded) { + reloadChildren(); + loaded = true; + } + } + + public VariableNode getChildAt(int index) { + ensureLoaded(); + Long parId = 0L; + if (thisVar != null && (thisVar.vType == VariableType.OBJECT || thisVar.vType == VariableType.MOVIECLIP)) { + parId = (Long) thisVar.value; + } + VariableNode vn = new VariableNode(level + 1, childs.get(index), parId, childTraits.get(index)); + vn.path.addAll(path); + vn.path.add(vn); + return vn; + } + + public int getChildCount() { + ensureLoaded(); + return childs.size(); + } + + public VariableNode(int level, Variable thisVar, Long parentId, Variable thisTrait) { + this.parentId = parentId; + this.thisVar = thisVar; + this.level = level; + this.thisTrait = thisTrait; + } + + public VariableNode(int level, Variable thisVar, Long parentId, Variable thisTrait, Long thisTraitId, List vars, List varTraits) { + this.parentId = parentId; + + this.thisVar = thisVar; + + this.level = level; + this.childs = vars; + + this.thisTrait = thisTrait; + + this.childTraits = varTraits; + this.path.add(this); + + loaded = true; + } + } + + public static class VariablesTableModel implements MyTreeTableModel { + + List tableListeners = new ArrayList<>(); + + VariableNode root; + + private final Map> nodeCache = new HashMap<>(); + + protected EventListenerList listenerList = new EventListenerList(); + + private static final int CHANGED = 0; + + private static final int INSERTED = 1; + + private static final int REMOVED = 2; + + private static final int STRUCTURE_CHANGED = 3; + + private final MyTreeTable ttable; + + public VariablesTableModel(MyTreeTable ttable, List vars, List parentIds) { + this.ttable = ttable; + List varTraits = new ArrayList<>(); + for (int i = 0; i < vars.size(); i++) { + varTraits.add(null); + } + root = new VariableNode(0, null, 0L, null, 0L, vars, varTraits); + } + + @Override + public int getColumnCount() { + return 3; + } + + @Override + public String getColumnName(int columnIndex) { + switch (columnIndex) { + case 0: + return AppStrings.translate("variables.column.name"); + case 1: + return AppStrings.translate("variables.column.type"); + case 2: + return AppStrings.translate("variables.column.value"); + default: + return null; + } + } + + @Override + public Class getColumnClass(int columnIndex) { + if (columnIndex == 0) { + return MyTreeTableModel.class; + } + return String.class; + } + + @Override + public Object getValueAt(Object node, int columnIndex) { + if (node == root) { + if (columnIndex == 0) { + return "root"; + } + return ""; + } + Variable v = ((VariableNode) node).thisVar; + + switch (columnIndex) { + case 0: + return v.name; + case 1: + String typeStr = v.getTypeAsStr(); + if ("Object".equals(typeStr)) { + typeStr = v.className; + } + if ("Object".equals(typeStr)) { + typeStr = v.typeName; + } + return typeStr; + case 2: + switch (v.vType) { + case VariableType.OBJECT: + case VariableType.MOVIECLIP: + case VariableType.FUNCTION: + return v.getTypeAsStr() + "(" + v.value + ")"; + case VariableType.STRING: + return "\"" + Helper.escapeActionScriptString("" + v.value) + "\""; + default: + return EcmaScript.toString(v.value); + } + + } + return null; + } + + @Override + public boolean isCellEditable(Object node, int column) { + return column == 0 || (column == 2 && node != root && ((VariableNode) node).thisVar.isPrimitive); + } + + @Override + public void setValueAt(Object aValue, Object node, int column) { + ActionScriptLexer lexer = new ActionScriptLexer(new StringReader("" + aValue)); + ParsedSymbol symb; + try { + symb = lexer.lex(); + ParsedSymbol f = lexer.yylex(); + if (f.type != SymbolType.EOF) { + return; + } + } catch (IOException | ActionParseException ex) { + return; + } + int valType; + switch (symb.type) { + case DOUBLE: + valType = VariableType.NUMBER; + break; + case INTEGER: + valType = VariableType.NUMBER; + break; + case NULL: + valType = VariableType.NULL; + break; + case STRING: + valType = VariableType.STRING; + break; + case UNDEFINED: + valType = VariableType.UNDEFINED; + break; + default: + return; + } + Main.getDebugHandler().setVariable(((VariableNode) node).parentId, ((VariableNode) node).thisVar.name, valType, symb.value); + //((VariableNode) node).refresh(); + Object[] path = new Object[((VariableNode) node).path.size()]; + for (int i = 0; i < path.length; i++) { + path[i] = ((VariableNode) node).path.get(i); + } + valueForPathChanged(new TreePath(path), aValue); + //fireTreeNodesChanged(this, path, new int[0]/*removed*/, new Object[]{node}); + } + + @Override + public Object getRoot() { + return root; + } + + @Override + public Object getChild(Object parent, int index) { + return ((VariableNode) parent).getChildAt(index); + } + + @Override + public int getChildCount(Object parent) { + int cnt = ((VariableNode) parent).getChildCount(); + return cnt; + } + + @Override + public boolean isLeaf(Object node) { + return getChildCount(node) == 0; + } + + @Override + public void valueForPathChanged(TreePath path, Object newValue) { + fireTreeNodesChanged(ttable, path.getParentPath().getPath(), new int[0], new Object[]{path.getLastPathComponent()}); + } + + @Override + public int getIndexOfChild(Object parent, Object child) { + int cnt = getChildCount(parent); + for (int i = 0; i < cnt; i++) { + if (getChild(parent, i) == child) { + return i; + } + } + return -1; + } + + @Override + public void addTreeModelListener(TreeModelListener l) { + listenerList.add(TreeModelListener.class, l); + } + + @Override + public void removeTreeModelListener(TreeModelListener l) { + listenerList.remove(TreeModelListener.class, l); + } + + protected void fireTreeNodesChanged(Object source, Object[] path, int[] childIndices, Object[] children) { + fireTreeNode(CHANGED, source, path, childIndices, children); + } + + protected void fireTreeNodesInserted(Object source, Object[] path, int[] childIndices, Object[] children) { + fireTreeNode(INSERTED, source, path, childIndices, children); + } + + protected void fireTreeNodesRemoved(Object source, Object[] path, int[] childIndices, Object[] children) { + fireTreeNode(REMOVED, source, path, childIndices, children); + } + + protected void fireTreeStructureChanged(Object source, Object[] path, int[] childIndices, Object[] children) { + fireTreeNode(STRUCTURE_CHANGED, source, path, childIndices, children); + } + + private void fireTreeNode(int changeType, Object source, Object[] path, int[] childIndices, Object[] children) { + Object[] listeners = listenerList.getListenerList(); + TreeModelEvent e = new TreeModelEvent(source, path, childIndices, children); + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == TreeModelListener.class) { + + switch (changeType) { + case CHANGED: + ((TreeModelListener) listeners[i + 1]).treeNodesChanged(e); + break; + case INSERTED: + ((TreeModelListener) listeners[i + 1]).treeNodesInserted(e); + break; + case REMOVED: + ((TreeModelListener) listeners[i + 1]).treeNodesRemoved(e); + break; + case STRUCTURE_CHANGED: + ((TreeModelListener) listeners[i + 1]).treeStructureChanged(e); + break; + default: + break; + } + + } + } + } + } + + public ABCPanel(MainPanel mainPanel) { + + this.mainPanel = mainPanel; + setLayout(new BorderLayout()); + + decompiledTextArea = new DecompiledEditorPane(this); + decompiledTextArea.addTextChangedListener(this::decompiledTextAreaTextChanged); + + decompiledTextArea.setLinkHandler(new LinkHandler() { + + @Override + public boolean isLink(Token token) { + return hasDeclaration(token.start); + } + + @Override + public void handleLink(Token token) { + gotoDeclaration(token.start); + } + + @Override + public Highlighter.HighlightPainter linkPainter() { + return decompiledTextArea.linkPainter(); + } + }); + + searchPanel = new SearchPanel<>(new FlowLayout(), this); + + decompiledScrollPane = new JScrollPane(decompiledTextArea); + + JPanel iconDecPanel = new JPanel(); + iconDecPanel.setLayout(new BoxLayout(iconDecPanel, BoxLayout.Y_AXIS)); + JPanel iconsPanel = new JPanel(); + iconsPanel.setLayout(new BoxLayout(iconsPanel, BoxLayout.X_AXIS)); + + JButton newTraitButton = new JButton(View.getIcon("traitadd16")); + newTraitButton.setMargin(new Insets(5, 5, 5, 5)); + newTraitButton.addActionListener(this::addTraitButtonActionPerformed); + newTraitButton.setToolTipText(AppStrings.translate("button.addtrait")); + iconsPanel.add(newTraitButton); + + scriptNameLabel = new JLabel("-"); + scriptNameLabel.setAlignmentX(0); + iconsPanel.setAlignmentX(0); + decompiledScrollPane.setAlignmentX(0); + iconDecPanel.add(scriptNameLabel); + iconDecPanel.add(iconsPanel); + iconDecPanel.add(decompiledScrollPane); + + final JPanel decButtonsPan = new JPanel(new FlowLayout()); + decButtonsPan.setBorder(new BevelBorder(BevelBorder.RAISED)); + decButtonsPan.add(editDecompiledButton); + decButtonsPan.add(experimentalLabel); + decButtonsPan.add(saveDecompiledButton); + decButtonsPan.add(cancelDecompiledButton); + + editDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + saveDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + cancelDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + + saveDecompiledButton.addActionListener(this::saveDecompiledButtonActionPerformed); + editDecompiledButton.addActionListener(this::editDecompiledButtonActionPerformed); + cancelDecompiledButton.addActionListener(this::cancelDecompiledButtonActionPerformed); + + saveDecompiledButton.setVisible(false); + cancelDecompiledButton.setVisible(false); + decButtonsPan.setAlignmentX(0); + + JPanel decPanel = new JPanel(new BorderLayout()); + decPanel.add(searchPanel, BorderLayout.NORTH); + decPanel.add(iconDecPanel, BorderLayout.CENTER); + decPanel.add(decButtonsPan, BorderLayout.SOUTH); + detailPanel = new DetailPanel(this); + JPanel panB = new JPanel(); + panB.setLayout(new BorderLayout()); + panB.add(decLabel, BorderLayout.NORTH); + + Main.getDebugHandler().addConnectionListener(new DebuggerHandler.ConnectionListener() { + + @Override + public void connected() { + decButtonsPan.setVisible(false); + } + + @Override + public void disconnected() { + decButtonsPan.setVisible(true); + } + }); + + debugPanel = new DebugPanel(); + + JPersistentSplitPane sp2; + + panB.add(sp2 = new JPersistentSplitPane(JSplitPane.VERTICAL_SPLIT, decPanel, debugPanel, Configuration.guiAvm2VarsSplitPaneDividerLocationPercent), BorderLayout.CENTER); + sp2.setContinuousLayout(true); + + debugPanel.setVisible(false); + + decLabel.setHorizontalAlignment(SwingConstants.CENTER); + //decLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + splitPane = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, panB, detailPanel, Configuration.guiAvm2SplitPaneDividerLocationPercent); + splitPane.setContinuousLayout(true); + + decompiledTextArea.changeContentType("text/actionscript"); + decompiledTextArea.setFont(new Font("Monospaced", Font.PLAIN, decompiledTextArea.getFont().getSize())); + + View.addEditorAction(decompiledTextArea, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + int multinameIndex = decompiledTextArea.getMultinameUnderCaret(); + if (multinameIndex > -1) { + UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, ABCPanel.this, false); + usageFrame.setVisible(true); + } + } + }, "find-usages", AppStrings.translate("abc.action.find-usages"), "control U"); + + View.addEditorAction(decompiledTextArea, new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + gotoDeclaration(decompiledTextArea.getCaretPosition()); + } + }, "find-declaration", AppStrings.translate("abc.action.find-declaration"), "control B"); + + CtrlClickHandler cch = new CtrlClickHandler(); + decompiledTextArea.addKeyListener(cch); + decompiledTextArea.addMouseListener(cch); + decompiledTextArea.addMouseMotionListener(cch); + + navigator = new TraitsList(this); + + navPanel = new JPanel(new BorderLayout()); + JPanel navIconsPanel = new JPanel(); + navIconsPanel.setLayout(new BoxLayout(navIconsPanel, BoxLayout.X_AXIS)); + final JToggleButton sortButton = new JToggleButton(View.getIcon("sort16")); + sortButton.setMargin(new Insets(3, 3, 3, 3)); + navIconsPanel.add(sortButton); + navPanel.add(navIconsPanel, BorderLayout.SOUTH); + navPanel.add(new JScrollPane(navigator), BorderLayout.CENTER); + sortButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + navigator.setSorted(sortButton.isSelected()); + navigator.updateUI(); + } + }); + + tabbedPane = new JTabbedPane(); + tabbedPane.addTab(AppStrings.translate("traits"), navPanel); + add(splitPane, BorderLayout.CENTER); + + JPanel panConstants = new JPanel(); + panConstants.setLayout(new BorderLayout()); + + constantTypeList = new JComboBox<>(new String[]{"UINT", "INT", "DOUBLE", "DECIMAL", "STRING", "NAMESPACE", "NAMESPACESET", "MULTINAME"}); + constantTable = new JTable(); + if (abc != null) { + View.autoResizeColWidth(constantTable, new UIntTableModel(abc)); + } + constantTable.setAutoCreateRowSorter(true); + + final ABCPanel t = this; + constantTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + if (constantTypeList.getSelectedIndex() == 7) { //MULTINAME + int rowIndex = constantTable.getSelectedRow(); + if (rowIndex == -1) { + return; + } + int multinameIndex = constantTable.convertRowIndexToModel(rowIndex); + if (multinameIndex > 0) { + UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, t, false); + usageFrame.setVisible(true); + } + } + } + } + }); + constantTypeList.addItemListener(this); + panConstants.add(constantTypeList, BorderLayout.NORTH); + panConstants.add(new JScrollPane(constantTable), BorderLayout.CENTER); + tabbedPane.addTab(AppStrings.translate("constants"), panConstants); + } + + private void decompiledTextAreaTextChanged() { + setModified(true); + } + + private boolean isModified() { + return saveDecompiledButton.isVisible() && saveDecompiledButton.isEnabled(); + } + + private void setModified(boolean value) { + saveDecompiledButton.setEnabled(value); + cancelDecompiledButton.setEnabled(value); + } + + private boolean hasDeclaration(int pos) { + if (decompiledTextArea == null) { + return false; //? + } + SyntaxDocument sd = (SyntaxDocument) decompiledTextArea.getDocument(); + Token t = sd.getTokenAt(pos + 1); + if (t == null || (t.type != TokenType.IDENTIFIER && t.type != TokenType.KEYWORD && t.type != TokenType.REGEX)) { + return false; + } + Reference abcIndex = new Reference<>(0); + Reference classIndex = new Reference<>(0); + Reference traitIndex = new Reference<>(0); + Reference multinameIndexRef = new Reference<>(0); + Reference classTrait = new Reference<>(false); + + if (decompiledTextArea.getPropertyTypeAtPos(pos, abcIndex, classIndex, traitIndex, classTrait, multinameIndexRef)) { + return true; + } + int multinameIndex = decompiledTextArea.getMultinameAtPos(pos); + if (multinameIndex > -1) { + if (multinameIndex == 0) { + return false; + } + List usages = abc.findMultinameDefinition(multinameIndex); + + Multiname m = abc.constants.getMultiname(multinameIndex); + + //search other ABC tags if this is not private multiname + if (m.getSingleNamespaceIndex(abc.constants) > 0 && abc.constants.getNamespace(m.getSingleNamespaceIndex(abc.constants)).kind != Namespace.KIND_PRIVATE) { + for (ABCContainerTag at : getAbcList()) { + ABC a = at.getABC(); + if (a == abc) { + continue; + } + + int mid = a.constants.getMultinameId(m, abc.constants); + if (mid > 0) { + usages.addAll(a.findMultinameDefinition(mid)); + } + } + } + + //more than one? display list + if (!usages.isEmpty()) { + return true; + } + } + + return decompiledTextArea.getLocalDeclarationOfPos(pos, new Reference<>(null)) != -1; + } + + private void gotoDeclaration(int pos) { + Reference abcIndex = new Reference<>(0); + Reference classIndex = new Reference<>(0); + Reference traitIndex = new Reference<>(0); + Reference classTrait = new Reference<>(false); + Reference multinameIndexRef = new Reference<>(0); + + if (decompiledTextArea.getPropertyTypeAtPos(pos, abcIndex, classIndex, traitIndex, classTrait, multinameIndexRef)) { + UsageFrame.gotoUsage(ABCPanel.this, new TraitMultinameUsage(getAbcList().get(abcIndex.getVal()).getABC(), multinameIndexRef.getVal(), classIndex.getVal(), traitIndex.getVal(), classTrait.getVal(), null, -1) { + }); + return; + } + int multinameIndex = decompiledTextArea.getMultinameAtPos(pos); + if (multinameIndex > -1) { + List usages = abc.findMultinameDefinition(multinameIndex); + + Multiname m = abc.constants.getMultiname(multinameIndex); + //search other ABC tags if this is not private multiname + if (m.getSingleNamespaceIndex(abc.constants) > 0 && m.getSingleNamespace(abc.constants).kind != Namespace.KIND_PRIVATE) { + for (ABCContainerTag at : getAbcList()) { + ABC a = at.getABC(); + if (a == abc) { + continue; + } + int mid = a.constants.getMultinameId(m, abc.constants); + if (mid > 0) { + usages.addAll(a.findMultinameDefinition(mid)); + } + } + } + + //more than one? display list + if (usages.size() > 1) { + UsageFrame usageFrame = new UsageFrame(abc, multinameIndex, ABCPanel.this, true); + usageFrame.setVisible(true); + return; + } else if (!usages.isEmpty()) { //one + UsageFrame.gotoUsage(ABCPanel.this, usages.get(0)); + return; + } + } + + int dpos = decompiledTextArea.getLocalDeclarationOfPos(pos, new Reference<>(null)); + if (dpos > -1) { + decompiledTextArea.setCaretPosition(dpos); + + } + + } + + private class CtrlClickHandler extends KeyAdapter implements MouseListener, MouseMotionListener { + + private boolean ctrlDown = false; + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == 17 && !decompiledTextArea.isEditable()) { + ctrlDown = true; + //decompiledTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } + } + + @Override + public void keyReleased(KeyEvent e) { + if (e.getKeyCode() == 17) { + ctrlDown = false; + //decompiledTextArea.setCursor(Cursor.getDefaultCursor()); + } + } + + @Override + public void mouseClicked(MouseEvent e) { + if (ctrlDown && e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 1 && !decompiledTextArea.isEditable()) { + ctrlDown = false; + //decompiledTextArea.setCursor(Cursor.getDefaultCursor()); + //gotoDeclaration(); + } + } + + @Override + public void mousePressed(MouseEvent e) { + + } + + @Override + public void mouseReleased(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + ctrlDown = false; + decompiledTextArea.setCursor(Cursor.getDefaultCursor()); + } + + @Override + public void mouseDragged(MouseEvent e) { + } + + @Override + public void mouseMoved(MouseEvent e) { + if (ctrlDown && decompiledTextArea.isEditable()) { + ctrlDown = false; + decompiledTextArea.setCursor(Cursor.getDefaultCursor()); + } + } + } + + public void reload() { + lastDecompiled = ""; + SWF swf = getSwf(); + if (swf != null) { + swf.clearScriptCache(); + } + + decompiledTextArea.reloadClass(); + detailPanel.methodTraitPanel.methodCodePanel.clear(); + } + + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getSource() == constantTypeList) { + int index = ((JComboBox) e.getSource()).getSelectedIndex(); + if (index == -1) { + return; + } + updateConstList(); + } + } + + public void display() { + setVisible(true); + } + + public void hilightScript(SWF swf, String name) { + TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); + TreeItem scriptsNode = ttm.getScriptsNode(swf); + if (scriptsNode instanceof ClassesListTreeModel) { + ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode; + ScriptPack pack = null; + for (ScriptPack item : clModel.getList()) { + if (!item.isSimple && Configuration.ignoreCLikePackages.get()) { + continue; + } + ClassPath classPath = item.getClassPath(); + + // first check the className to avoid calling unnecessary toString + if (name.endsWith(classPath.className) && classPath.toRawString().equals(name)) { + pack = item; + break; + } + } + if (pack != null) { + hilightScript(pack); + } + } + } + + public void hilightScript(ScriptPack pack) { + TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); + TreePath tp0 = ttm.getTreePath(pack); + if (tp0 == null) { + mainPanel.closeTagTreeSearch(); + tp0 = ttm.getTreePath(pack); + } + final TreePath tp = tp0; + View.execInEventDispatchLater(() -> { + mainPanel.tagTree.setSelectionPath(tp); + mainPanel.tagTree.scrollPathToVisible(tp); + }); + + } + + @Override + public void updateSearchPos(ABCPanelSearchResult item) { + ScriptPack pack = item.getScriptPack(); + setAbc(pack.abc); + decompiledTextArea.setScript(pack, false); + hilightScript(pack); + decompiledTextArea.setCaretPosition(0); + + View.execInEventDispatchLater(() -> { + searchPanel.showQuickFindDialog(decompiledTextArea); + }); + + } + + public boolean isDirectEditing() { + return saveDecompiledButton.isVisible() && saveDecompiledButton.isEnabled(); + } + + public void setDecompiledEditMode(boolean val) { + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + if (val) { + lastDecompiled = decompiledTextArea.getText(); + } else { + decompiledTextArea.setText(lastDecompiled); + } + + decompiledTextArea.setEditable(val); + saveDecompiledButton.setVisible(val); + saveDecompiledButton.setEnabled(false); + editDecompiledButton.setVisible(!val); + experimentalLabel.setVisible(!val); + cancelDecompiledButton.setVisible(val); + decompiledTextArea.getCaret().setVisible(true); + decLabel.setIcon(val ? View.getIcon("editing16") : null); + detailPanel.setVisible(!val); + + decompiledTextArea.ignoreCarret = val; + decompiledTextArea.requestFocusInWindow(); + } + }); + + } + + private void editDecompiledButtonActionPerformed(ActionEvent evt) { + File swc = Configuration.getPlayerSWC(); + if (swc == null || !swc.exists()) { + if (View.showConfirmDialog(this, AppStrings.translate("message.playerpath.lib.notset"), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE) == JOptionPane.OK_OPTION) { + Main.advancedSettings("paths"); + return; + } + } + if (View.showConfirmDialog(null, AppStrings.translate("message.confirm.experimental.function"), AppStrings.translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, Configuration.warningExperimentalAS3Edit, JOptionPane.OK_OPTION) == JOptionPane.OK_OPTION) { + setDecompiledEditMode(true); + } + } + + private void cancelDecompiledButtonActionPerformed(ActionEvent evt) { + setDecompiledEditMode(false); + } + + private void saveDecompiledButtonActionPerformed(ActionEvent evt) { + ScriptPack pack = decompiledTextArea.getScriptLeaf(); + int oldIndex = pack.scriptIndex; + SWF.uncache(pack); + + try { + String oldSp = pack.getClassPath().toRawString(); + /*List packs = abc.script_info.get(oldIndex).getPacks(abc, oldIndex, null, pack.allABCs); + if (!packs.isEmpty()) { + + }*/ + + String as = decompiledTextArea.getText(); + abc.replaceScriptPack(pack, as); + lastDecompiled = as; + setDecompiledEditMode(false); + mainPanel.updateClassesList(); + + if (oldSp != null) { + hilightScript(getSwf(), oldSp); + } + + reload(); + View.showMessageDialog(this, AppStrings.translate("message.action.saved"), AppStrings.translate("dialog.message.title"), JOptionPane.INFORMATION_MESSAGE, Configuration.showCodeSavedMessage); + } catch (AVM2ParseException ex) { + abc.script_info.get(oldIndex).delete(abc, false); + decompiledTextArea.gotoLine((int) ex.line); + decompiledTextArea.markError(); + View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } catch (CompilationException ex) { + abc.script_info.get(oldIndex).delete(abc, false); + decompiledTextArea.gotoLine((int) ex.line); + decompiledTextArea.markError(); + View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + + } catch (Throwable ex) { + Logger.getLogger(ABCPanel.class + .getName()).log(Level.SEVERE, null, ex); + } + } + + private void addTraitButtonActionPerformed(ActionEvent evt) { + int class_index = decompiledTextArea.getClassIndex(); + if (class_index < 0) { + return; + } + if (newTraitDialog == null) { + newTraitDialog = new NewTraitDialog(); + } + int void_type = abc.constants.getPublicQnameId("void", true);//abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("void"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); + int int_type = abc.constants.getPublicQnameId("int", true); //abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("int"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); + + Trait t = null; + int kind; + int nskind; + String name = null; + boolean isStatic; + Multiname m; + + boolean again = false; + loopm: + do { + if (again) { + View.showMessageDialog(null, AppStrings.translate("error.trait.exists").replace("%name%", name), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + again = false; + if (newTraitDialog.showDialog() != AppDialog.OK_OPTION) { + return; + } + kind = newTraitDialog.getTraitType(); + nskind = newTraitDialog.getNamespaceKind(); + name = newTraitDialog.getTraitName(); + isStatic = newTraitDialog.getStatic(); + m = Multiname.createQName(false, abc.constants.getStringId(name, true), abc.constants.getNamespaceId(nskind, "", 0, true)); + int mid = abc.constants.getMultinameId(m, false); + if (mid <= 0) { + break; + } + for (Trait tr : abc.class_info.get(class_index).static_traits.traits) { + if (tr.name_index == mid) { + again = true; + break; + } + } + + for (Trait tr : abc.instance_info.get(class_index).instance_traits.traits) { + if (tr.name_index == mid) { + again = true; + break; + } + } + } while (again); + switch (kind) { + case Trait.TRAIT_GETTER: + case Trait.TRAIT_SETTER: + case Trait.TRAIT_METHOD: + TraitMethodGetterSetter tm = new TraitMethodGetterSetter(); + MethodInfo mi = new MethodInfo(new int[0], void_type, abc.constants.getStringId(name, true), 0, new ValueKind[0], new int[0]); + int method_info = abc.addMethodInfo(mi); + tm.method_info = method_info; + MethodBody body = new MethodBody(abc, new Traits(), new byte[0], new ABCException[0]); + body.method_info = method_info; + body.init_scope_depth = 1; + body.max_regs = 1; + body.max_scope_depth = 1; + body.max_stack = 1; + body.exceptions = new ABCException[0]; + AVM2Code code = new AVM2Code(); + code.code.add(new AVM2Instruction(0, AVM2Instructions.GetLocal0, null)); + code.code.add(new AVM2Instruction(0, AVM2Instructions.PushScope, null)); + code.code.add(new AVM2Instruction(0, AVM2Instructions.ReturnVoid, null)); + body.setCode(code); + Traits traits = new Traits(); + traits.traits = new ArrayList<>(); + body.traits = traits; + abc.addMethodBody(body); + t = tm; + break; + case Trait.TRAIT_SLOT: + case Trait.TRAIT_CONST: + TraitSlotConst ts = new TraitSlotConst(); + ts.type_index = int_type; + ts.value_kind = ValueKind.CONSTANT_Int; + ts.value_index = abc.constants.getIntId(0, true); + t = ts; + break; + } + if (t != null) { + t.kindType = kind; + t.name_index = abc.constants.getMultinameId(m, true); + int traitId; + if (isStatic) { + traitId = abc.class_info.get(class_index).static_traits.addTrait(t); + } else { + traitId = abc.class_info.get(class_index).static_traits.traits.size() + abc.instance_info.get(class_index).instance_traits.addTrait(t); + } + int scriptIndex = decompiledTextArea.getScriptLeaf().scriptIndex; + if (scriptIndex >= 0 && scriptIndex < abc.script_info.size()) { + abc.script_info.get(scriptIndex).setModified(true); + } + ((Tag) abc.parentTag).setModified(true); + reload(); + decompiledTextArea.gotoTrait(traitId); + } + } + + @Override + public boolean tryAutoSave() { + // todo: implement + return false; + } + + @Override + public boolean isEditing() { + return detailPanel.isEditing() || isModified(); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/abc/NewTraitDialog.java b/src/com/jpexs/decompiler/flash/gui/abc/NewTraitDialog.java index f40d3cea8..0b53a278e 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/NewTraitDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/NewTraitDialog.java @@ -1,184 +1,184 @@ -/* - * Copyright (C) 2010-2016 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.abc; - -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.gui.AppDialog; -import com.jpexs.decompiler.flash.gui.AppStrings; -import com.jpexs.decompiler.flash.gui.View; -import java.awt.BorderLayout; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.event.ActionEvent; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JComponent; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; -import javax.swing.event.AncestorEvent; -import javax.swing.event.AncestorListener; - -/** - * - * @author JPEXS - */ -public class NewTraitDialog extends AppDialog { - - private static final int[] modifiers = new int[]{ - Namespace.KIND_PACKAGE, - Namespace.KIND_PRIVATE, - Namespace.KIND_PROTECTED, - Namespace.KIND_NAMESPACE, - Namespace.KIND_PACKAGE_INTERNAL, - Namespace.KIND_EXPLICIT, - Namespace.KIND_STATIC_PROTECTED - }; - - private static final int[] types = new int[]{ - Trait.TRAIT_METHOD, - Trait.TRAIT_GETTER, - Trait.TRAIT_SETTER, - Trait.TRAIT_CONST, - Trait.TRAIT_SLOT - }; - - private final JComboBox accessComboBox; - - private final JComboBox typeComboBox; - - private final JCheckBox staticCheckbox; - - private final JTextField nameField; - - private int result = ERROR_OPTION; - - public boolean getStatic() { - return staticCheckbox.isSelected(); - } - - public int getNamespaceKind() { - return modifiers[accessComboBox.getSelectedIndex()]; - } - - public int getTraitType() { - return types[typeComboBox.getSelectedIndex()]; - } - - public String getTraitName() { - return nameField.getText(); - } - - public NewTraitDialog() { - setSize(500, 300); - setTitle(translate("dialog.title")); - View.centerScreen(this); - View.setWindowIcon(this); - Container cnt = getContentPane(); - cnt.setLayout(new BorderLayout()); - JPanel optionsPanel = new JPanel(new FlowLayout()); - //optionsPanel.add(new JLabel(translate("label.type"))); - typeComboBox = new JComboBox<>(new String[]{ - translate("type.method"), - translate("type.getter"), - translate("type.setter"), - translate("type.const"), - translate("type.slot"),}); - staticCheckbox = new JCheckBox(translate("checkbox.static")); - optionsPanel.add(staticCheckbox); - String[] accessStrings = new String[modifiers.length]; - for (int i = 0; i < accessStrings.length; i++) { - String pref = Namespace.kindToPrefix(modifiers[i]); - String name = Namespace.kindToStr(modifiers[i]); - accessStrings[i] = (pref.isEmpty() ? "" : pref + " ") + "(" + name + ")"; - } - - //optionsPanel.add(new JLabel(translate("label.access"))); - accessComboBox = new JComboBox<>(accessStrings); - optionsPanel.add(accessComboBox); - - optionsPanel.add(typeComboBox); - - //optionsPanel.add(new JLabel(translate("label.name"))); - nameField = new JTextField(); - nameField.setPreferredSize(new Dimension(300, nameField.getPreferredSize().height)); - optionsPanel.add(nameField); - - cnt.add(optionsPanel, BorderLayout.CENTER); - JPanel buttonsPanel = new JPanel(new FlowLayout()); - JButton buttonOk = new JButton(AppStrings.translate("button.ok")); - buttonOk.addActionListener(this::okButtonActionPerformed); - JButton buttonCancel = new JButton(AppStrings.translate("button.cancel")); - buttonCancel.addActionListener(this::cancelButtonActionPerformed); - buttonsPanel.add(buttonOk); - buttonsPanel.add(buttonCancel); - cnt.add(buttonsPanel, BorderLayout.SOUTH); - pack(); - setDefaultCloseOperation(HIDE_ON_CLOSE); - setModalityType(ModalityType.APPLICATION_MODAL); - - nameField.addAncestorListener(new AncestorListener() { - @Override - public void ancestorAdded(AncestorEvent event) { - JComponent component = event.getComponent(); - component.requestFocusInWindow(); - } - - @Override - public void ancestorRemoved(AncestorEvent event) { - } - - @Override - public void ancestorMoved(AncestorEvent event) { - } - }); - getRootPane().setDefaultButton(buttonOk); - } - - @Override - public void setVisible(boolean b) { - if (b) { - result = ERROR_OPTION; - nameField.setText(""); - } - - super.setVisible(b); - } - - private void okButtonActionPerformed(ActionEvent evt) { - result = OK_OPTION; - if (nameField.getText().trim().isEmpty()) { - View.showMessageDialog(null, translate("error.name"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - return; - } - - setVisible(false); - } - - private void cancelButtonActionPerformed(ActionEvent evt) { - result = CANCEL_OPTION; - setVisible(false); - } - - public int showDialog() { - setVisible(true); - return result; - } -} +/* + * Copyright (C) 2010-2016 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.abc; + +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.gui.AppDialog; +import com.jpexs.decompiler.flash.gui.AppStrings; +import com.jpexs.decompiler.flash.gui.View; +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.event.AncestorEvent; +import javax.swing.event.AncestorListener; + +/** + * + * @author JPEXS + */ +public class NewTraitDialog extends AppDialog { + + private static final int[] modifiers = new int[]{ + Namespace.KIND_PACKAGE, + Namespace.KIND_PRIVATE, + Namespace.KIND_PROTECTED, + Namespace.KIND_NAMESPACE, + Namespace.KIND_PACKAGE_INTERNAL, + Namespace.KIND_EXPLICIT, + Namespace.KIND_STATIC_PROTECTED + }; + + private static final int[] types = new int[]{ + Trait.TRAIT_METHOD, + Trait.TRAIT_GETTER, + Trait.TRAIT_SETTER, + Trait.TRAIT_CONST, + Trait.TRAIT_SLOT + }; + + private final JComboBox accessComboBox; + + private final JComboBox typeComboBox; + + private final JCheckBox staticCheckbox; + + private final JTextField nameField; + + private int result = ERROR_OPTION; + + public boolean getStatic() { + return staticCheckbox.isSelected(); + } + + public int getNamespaceKind() { + return modifiers[accessComboBox.getSelectedIndex()]; + } + + public int getTraitType() { + return types[typeComboBox.getSelectedIndex()]; + } + + public String getTraitName() { + return nameField.getText(); + } + + public NewTraitDialog() { + setSize(500, 300); + setTitle(translate("dialog.title")); + View.centerScreen(this); + View.setWindowIcon(this); + Container cnt = getContentPane(); + cnt.setLayout(new BorderLayout()); + JPanel optionsPanel = new JPanel(new FlowLayout()); + //optionsPanel.add(new JLabel(translate("label.type"))); + typeComboBox = new JComboBox<>(new String[]{ + translate("type.method"), + translate("type.getter"), + translate("type.setter"), + translate("type.const"), + translate("type.slot"),}); + staticCheckbox = new JCheckBox(translate("checkbox.static")); + optionsPanel.add(staticCheckbox); + String[] accessStrings = new String[modifiers.length]; + for (int i = 0; i < accessStrings.length; i++) { + String pref = Namespace.kindToPrefix(modifiers[i]); + String name = Namespace.kindToStr(modifiers[i]); + accessStrings[i] = (pref.isEmpty() ? "" : pref + " ") + "(" + name + ")"; + } + + //optionsPanel.add(new JLabel(translate("label.access"))); + accessComboBox = new JComboBox<>(accessStrings); + optionsPanel.add(accessComboBox); + + optionsPanel.add(typeComboBox); + + //optionsPanel.add(new JLabel(translate("label.name"))); + nameField = new JTextField(); + nameField.setPreferredSize(new Dimension(300, nameField.getPreferredSize().height)); + optionsPanel.add(nameField); + + cnt.add(optionsPanel, BorderLayout.CENTER); + JPanel buttonsPanel = new JPanel(new FlowLayout()); + JButton buttonOk = new JButton(AppStrings.translate("button.ok")); + buttonOk.addActionListener(this::okButtonActionPerformed); + JButton buttonCancel = new JButton(AppStrings.translate("button.cancel")); + buttonCancel.addActionListener(this::cancelButtonActionPerformed); + buttonsPanel.add(buttonOk); + buttonsPanel.add(buttonCancel); + cnt.add(buttonsPanel, BorderLayout.SOUTH); + pack(); + setDefaultCloseOperation(HIDE_ON_CLOSE); + setModalityType(ModalityType.APPLICATION_MODAL); + + nameField.addAncestorListener(new AncestorListener() { + @Override + public void ancestorAdded(AncestorEvent event) { + JComponent component = event.getComponent(); + component.requestFocusInWindow(); + } + + @Override + public void ancestorRemoved(AncestorEvent event) { + } + + @Override + public void ancestorMoved(AncestorEvent event) { + } + }); + getRootPane().setDefaultButton(buttonOk); + } + + @Override + public void setVisible(boolean b) { + if (b) { + result = ERROR_OPTION; + nameField.setText(""); + } + + super.setVisible(b); + } + + private void okButtonActionPerformed(ActionEvent evt) { + result = OK_OPTION; + if (nameField.getText().trim().isEmpty()) { + View.showMessageDialog(null, translate("error.name"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + return; + } + + setVisible(false); + } + + private void cancelButtonActionPerformed(ActionEvent evt) { + result = CANCEL_OPTION; + setVisible(false); + } + + public int showDialog() { + setVisible(true); + return result; + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/debugger/Debugger.java b/src/com/jpexs/decompiler/flash/gui/debugger/Debugger.java index 234bbf1bf..e10900a78 100644 --- a/src/com/jpexs/decompiler/flash/gui/debugger/Debugger.java +++ b/src/com/jpexs/decompiler/flash/gui/debugger/Debugger.java @@ -1,308 +1,308 @@ -/* - * Copyright (C) 2010-2016 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.debugger; - -import com.jpexs.helpers.utf8.Utf8Helper; -import java.io.ByteArrayOutputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.WeakHashMap; - -/** - * - * @author JPEXS - */ -public class Debugger { - - private static final Set listeners = new HashSet<>(); - - public synchronized void addMessageListener(DebugListener l) { - listeners.add(l); - } - - public synchronized void removeMessageListener(DebugListener l) { - listeners.remove(l); - } - - private static class DebugHandler extends Thread { - - private final Socket s; - - private final int serverPort; - - private static int maxid = 0; - - private final int id; - - public boolean finished = false; - - private final Map parameters = new HashMap<>(); - - public static final int MSG_STRING = 0; - - public static final int MSG_LOADER_URL = 1; - - public static final int MSG_LOADER_BYTES = 2; - - public String getParameter(String name, String defValue) { - if (parameters.containsKey(name)) { - return parameters.get(name); - } - return defValue; - } - - public int getVersionMajor() { - return Integer.parseInt(getParameter("debug.version.major", "1")); - } - - public int getVersionMinor() { - return Integer.parseInt(getParameter("debug.version.major", "0")); - } - - public boolean hasMsgType() { - return getVersionMajor() > 1 || getVersionMinor() > 0; - } - - public DebugHandler(int serverPort, Socket s) { - this.s = s; - id = maxid++; - this.serverPort = serverPort; - } - - public void cancel() { - try { - s.close(); - } catch (IOException ex) { - //ignore - } - } - - private int readType(InputStream is) throws IOException { - int type = is.read(); - if (type == -1) { - throw new EOFException(); - } - return type; - } - - private byte[] readBytes(InputStream is) throws IOException { - int len = is.read(); - if (len == -1) { - throw new EOFException(); - } - int len2 = is.read(); - if (len2 == -1) { - throw new EOFException(); - } - int len3 = is.read(); - if (len3 == -1) { - throw new EOFException(); - } - int len4 = is.read(); - if (len4 == -1) { - throw new EOFException(); - } - len = (len << 24) + (len2 << 16) + (len3 << 8) + len4; - byte[] data = new byte[len]; - int cnt; - int off = 0; - while (len > 0 && (cnt = is.read(data, off, len)) > 0) { - len -= cnt; - off += cnt; - } - return data; - } - - private String readString(InputStream is) throws IOException { - int len = is.read(); - if (len == -1) { - throw new EOFException(); - } - int len2 = is.read(); - if (len2 == -1) { - throw new EOFException(); - } - len = (len << 8) + len2; - - byte[] buf = new byte[len]; - for (int i = 0; i < len; i++) { - int rd = is.read(); - if (rd == -1) { - throw new EOFException(); - } - buf[i] = (byte) rd; - } - return new String(buf, Utf8Helper.charset); - } - - @Override - public void run() { - String clientName = Integer.toString(id); - try (InputStream is = s.getInputStream()) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - int c; - do { - c = is.read(); - if (c != 0) { - baos.write(c); - } - - } while (c > 0); - String ret = baos.toString("UTF-8"); - - if (ret.equals("")) { - try (OutputStream os = s.getOutputStream()) { - os.write(("").getBytes("UTF-8")); - } - } else { - if (!ret.isEmpty()) { - String[] param = (ret.contains(";") ? ret.split(";") : new String[]{ret}); - for (String p : param) { - if (p.contains("=")) { - String key = p.substring(0, p.indexOf('=')); - String val = p.substring(p.indexOf('=') + 1); - parameters.put(key, val); - } else { - parameters.put(p, "true"); - } - } - } - boolean hasType = hasMsgType(); - String name = readString(is); - if (!name.isEmpty()) { - clientName = name; - } - while (true) { - int type = 0; - if (hasType) { - type = readType(is); - } - switch (type) { - case MSG_STRING: - ret = readString(is); - for (DebugListener l : listeners) { - l.onMessage(clientName, ret); - } - break; - case MSG_LOADER_URL: - ret = readString(is); - for (DebugListener l : listeners) { - l.onLoaderURL(clientName, ret); - } - break; - case MSG_LOADER_BYTES: - byte[] retB = readBytes(is); - for (DebugListener l : listeners) { - l.onLoaderBytes(clientName, retB); - } - break; - } - } - } - - } catch (IOException ex) { - //ignore - } - try { - s.close(); - } catch (IOException ex) { - //ignore - } - finished = true; - for (DebugListener l : listeners) { - l.onFinish(clientName); - } - } - } - - private static class DebugServerThread extends Thread { - - private final int port; - - private ServerSocket ss; - - private final Map handlers = new WeakHashMap<>(); - - public DebugServerThread(int port) { - this.port = port; - } - - @Override - public void run() { - try { - ss = new ServerSocket(port, 50, InetAddress.getByName("localhost")); - ss.setReuseAddress(true); - while (true) { - Socket s = ss.accept(); - DebugHandler h = new DebugHandler(port, s); - handlers.put(h.id, h); - h.start(); - } - } catch (IOException ex) { - //ignore - } - } - } - - private final int port; - - public Debugger(int port) { - this.port = port; - } - - private DebugServerThread server = null; - - public synchronized void start() { - if (server == null) { - server = new DebugServerThread(port); - server.start(); - } - } - - public synchronized boolean isRunning() { - return server != null; - } - - public int getPort() { - return port; - } - - public synchronized void stop() { - if (server != null) { - try { - server.ss.close(); - } catch (IOException ex) { - //ignore - } - for (DebugHandler h : server.handlers.values()) { - h.cancel(); - } - server.handlers.clear(); - server = null; - } - } -} +/* + * Copyright (C) 2010-2016 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.debugger; + +import com.jpexs.helpers.utf8.Utf8Helper; +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; + +/** + * + * @author JPEXS + */ +public class Debugger { + + private static final Set listeners = new HashSet<>(); + + public synchronized void addMessageListener(DebugListener l) { + listeners.add(l); + } + + public synchronized void removeMessageListener(DebugListener l) { + listeners.remove(l); + } + + private static class DebugHandler extends Thread { + + private final Socket s; + + private final int serverPort; + + private static int maxid = 0; + + private final int id; + + public boolean finished = false; + + private final Map parameters = new HashMap<>(); + + public static final int MSG_STRING = 0; + + public static final int MSG_LOADER_URL = 1; + + public static final int MSG_LOADER_BYTES = 2; + + public String getParameter(String name, String defValue) { + if (parameters.containsKey(name)) { + return parameters.get(name); + } + return defValue; + } + + public int getVersionMajor() { + return Integer.parseInt(getParameter("debug.version.major", "1")); + } + + public int getVersionMinor() { + return Integer.parseInt(getParameter("debug.version.major", "0")); + } + + public boolean hasMsgType() { + return getVersionMajor() > 1 || getVersionMinor() > 0; + } + + public DebugHandler(int serverPort, Socket s) { + this.s = s; + id = maxid++; + this.serverPort = serverPort; + } + + public void cancel() { + try { + s.close(); + } catch (IOException ex) { + //ignore + } + } + + private int readType(InputStream is) throws IOException { + int type = is.read(); + if (type == -1) { + throw new EOFException(); + } + return type; + } + + private byte[] readBytes(InputStream is) throws IOException { + int len = is.read(); + if (len == -1) { + throw new EOFException(); + } + int len2 = is.read(); + if (len2 == -1) { + throw new EOFException(); + } + int len3 = is.read(); + if (len3 == -1) { + throw new EOFException(); + } + int len4 = is.read(); + if (len4 == -1) { + throw new EOFException(); + } + len = (len << 24) + (len2 << 16) + (len3 << 8) + len4; + byte[] data = new byte[len]; + int cnt; + int off = 0; + while (len > 0 && (cnt = is.read(data, off, len)) > 0) { + len -= cnt; + off += cnt; + } + return data; + } + + private String readString(InputStream is) throws IOException { + int len = is.read(); + if (len == -1) { + throw new EOFException(); + } + int len2 = is.read(); + if (len2 == -1) { + throw new EOFException(); + } + len = (len << 8) + len2; + + byte[] buf = new byte[len]; + for (int i = 0; i < len; i++) { + int rd = is.read(); + if (rd == -1) { + throw new EOFException(); + } + buf[i] = (byte) rd; + } + return new String(buf, Utf8Helper.charset); + } + + @Override + public void run() { + String clientName = Integer.toString(id); + try (InputStream is = s.getInputStream()) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + int c; + do { + c = is.read(); + if (c != 0) { + baos.write(c); + } + + } while (c > 0); + String ret = baos.toString("UTF-8"); + + if (ret.equals("")) { + try (OutputStream os = s.getOutputStream()) { + os.write(("").getBytes("UTF-8")); + } + } else { + if (!ret.isEmpty()) { + String[] param = (ret.contains(";") ? ret.split(";") : new String[]{ret}); + for (String p : param) { + if (p.contains("=")) { + String key = p.substring(0, p.indexOf('=')); + String val = p.substring(p.indexOf('=') + 1); + parameters.put(key, val); + } else { + parameters.put(p, "true"); + } + } + } + boolean hasType = hasMsgType(); + String name = readString(is); + if (!name.isEmpty()) { + clientName = name; + } + while (true) { + int type = 0; + if (hasType) { + type = readType(is); + } + switch (type) { + case MSG_STRING: + ret = readString(is); + for (DebugListener l : listeners) { + l.onMessage(clientName, ret); + } + break; + case MSG_LOADER_URL: + ret = readString(is); + for (DebugListener l : listeners) { + l.onLoaderURL(clientName, ret); + } + break; + case MSG_LOADER_BYTES: + byte[] retB = readBytes(is); + for (DebugListener l : listeners) { + l.onLoaderBytes(clientName, retB); + } + break; + } + } + } + + } catch (IOException ex) { + //ignore + } + try { + s.close(); + } catch (IOException ex) { + //ignore + } + finished = true; + for (DebugListener l : listeners) { + l.onFinish(clientName); + } + } + } + + private static class DebugServerThread extends Thread { + + private final int port; + + private ServerSocket ss; + + private final Map handlers = new WeakHashMap<>(); + + public DebugServerThread(int port) { + this.port = port; + } + + @Override + public void run() { + try { + ss = new ServerSocket(port, 50, InetAddress.getByName("localhost")); + ss.setReuseAddress(true); + while (true) { + Socket s = ss.accept(); + DebugHandler h = new DebugHandler(port, s); + handlers.put(h.id, h); + h.start(); + } + } catch (IOException ex) { + //ignore + } + } + } + + private final int port; + + public Debugger(int port) { + this.port = port; + } + + private DebugServerThread server = null; + + public synchronized void start() { + if (server == null) { + server = new DebugServerThread(port); + server.start(); + } + } + + public synchronized boolean isRunning() { + return server != null; + } + + public int getPort() { + return port; + } + + public synchronized void stop() { + if (server != null) { + try { + server.ss.close(); + } catch (IOException ex) { + //ignore + } + for (DebugHandler h : server.handlers.values()) { + h.cancel(); + } + server.handlers.clear(); + server = null; + } + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java b/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java index 7d38de9a4..e06f83a3d 100644 --- a/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java +++ b/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java @@ -1,247 +1,247 @@ -/* - * Copyright (C) 2010-2016 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.debugger; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.gui.DebugLogDialog; -import com.jpexs.decompiler.flash.gui.Main; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.tags.FileAttributesTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.helpers.Helper; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Pattern; - -/** - * - * @author JPEXS - */ -public class DebuggerTools { - - private static final Logger logger = Logger.getLogger(DebuggerTools.class.getName()); - - public static final String DEBUGGER_PACKAGE = "com.jpexs.decompiler.flash.debugger"; - - private static volatile Debugger debugger; - - private static ScriptPack getDebuggerScriptPack(SWF swf) { - List allAbcList = new ArrayList<>(); - for (ABCContainerTag ac : swf.getAbcList()) { - allAbcList.add(ac.getABC()); - } - for (ABCContainerTag ac : swf.getAbcList()) { - ABC a = ac.getABC(); - for (ScriptPack m : a.getScriptPacks(DEBUGGER_PACKAGE, allAbcList)) { - if (isDebuggerClass(m.getClassPath().packageStr.toRawString(), null)) { - return m; - } - } - } - return null; - } - - public static boolean hasDebugger(SWF swf) { - return getDebuggerScriptPack(swf) != null; - } - - private static boolean isDebuggerClass(String tested, String cls) { - if (tested == null) { - return false; - } - - // fast check, because dynamic regex compile and match is expensive - if (!tested.startsWith(DEBUGGER_PACKAGE)) { - return false; - } - - if (cls == null) { - cls = ""; - } else { - cls = "\\." + Pattern.quote(cls); - } - - return tested.matches(Pattern.quote(DEBUGGER_PACKAGE) + "(\\.pkg[a-f0-9]+)?" + cls); - } - - public static void injectDebugLoader(SWF swf) { - if (hasDebugger(swf)) { - ScriptPack dsp = getDebuggerScriptPack(swf); - String debuggerPkg = dsp.getClassPath().packageStr.toRawString(); - for (ABCContainerTag ct : swf.getAbcList()) { - ABC a = ct.getABC(); - if (dsp.abc == a) { //do not replace Loader in debugger itself - continue; - } - for (int i = 1; i < a.constants.getMultinameCount(); i++) { - Multiname m = a.constants.getMultiname(i); - if ("flash.display.Loader".equals(m.getNameWithNamespace(a.constants).toRawString())) { - m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true); - m.name_index = a.constants.getStringId("DebugLoader", true); - ((Tag) ct).setModified(true); - } else if ("flash.utils.getDefinitionByName".equals(m.getNameWithNamespace(a.constants).toRawString())) { - m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true); - m.name_index = a.constants.getStringId("debugGetDefinitionByName", true); - ((Tag) ct).setModified(true); - } else if ("flash.utils.getQualifiedClassName".equals(m.getNameWithNamespace(a.constants).toRawString())) { - m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true); - m.name_index = a.constants.getStringId("debugGetQualifiedClassName", true); - ((Tag) ct).setModified(true); - } else if ("flash.utils.getQualifiedSuperclassName".equals(m.getNameWithNamespace(a.constants).toRawString())) { - m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true); - m.name_index = a.constants.getStringId("debugGetQualifiedSuperclassName", true); - ((Tag) ct).setModified(true); - } else if ("flash.utils.describeType".equals(m.getNameWithNamespace(a.constants).toRawString())) { - m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true); - m.name_index = a.constants.getStringId("debugDescribeType", true); - ((Tag) ct).setModified(true); - } - } - } - } - } - - public static void replaceTraceCalls(SWF swf, String fname) { - if (hasDebugger(swf)) { - String debuggerPkg = getDebuggerScriptPack(swf).getClassPath().packageStr.toRawString(); - //change trace to fname - for (ABCContainerTag ct : swf.getAbcList()) { - ABC a = ct.getABC(); - for (int i = 1; i < a.constants.getMultinameCount(); i++) { - Multiname m = a.constants.getMultiname(i); - if ("trace".equals(m.getNameWithNamespace(a.constants).toRawString())) { - m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true); - m.name_index = a.constants.getStringId(fname, true); - ((Tag) ct).setModified(true); - } - } - } - } - } - - public static void switchDebugger(SWF swf) { - int port = Configuration.debuggerPort.get(); - ScriptPack found = getDebuggerScriptPack(swf); - if (found != null) { - ABCContainerTag tag = found.abc.parentTag; - swf.removeTag((Tag) tag); - swf.getAbcList().remove(tag); - - //Change all debugger calls to normal trace / Loader - for (ABCContainerTag ct : swf.getAbcList()) { - ABC a = ct.getABC(); - for (int i = 1; i < a.constants.getMultinameCount(); i++) { - Multiname m = a.constants.getMultiname(i); - String packageStr = m.getNameWithNamespace(a.constants).toString(); - if (isDebuggerClass(packageStr, "debugTrace") - || isDebuggerClass(packageStr, "debugAlert") - || isDebuggerClass(packageStr, "debugSocket") - || isDebuggerClass(packageStr, "debugConsole")) { - m.name_index = a.constants.getStringId("trace", true); - m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, "", 0, true); - ((Tag) ct).setModified(true); - } else if (isDebuggerClass(packageStr, "DebugLoader")) { - m.name_index = a.constants.getStringId("Loader", true); - m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, "flash.display", 0, true); - } - } - } - } else { - Random rnd = new Random(); - byte[] rb = new byte[16]; - rnd.nextBytes(rb); - String rhex = Helper.byteArrayToHex(rb); - try { - //load debug swf - SWF debugSWF = new SWF(Main.class.getClassLoader().getResourceAsStream("com/jpexs/decompiler/flash/gui/debugger/debug.swf"), false); - - List al = swf.getAbcList(); - ABCContainerTag firstAbc = al.isEmpty() ? null : al.get(0); - if (firstAbc == null) { //nothing to instrument? - return; - } - String newdebuggerpkg = DEBUGGER_PACKAGE; - - if (Configuration.randomDebuggerPackage.get()) { - newdebuggerpkg += ".pkg" + rhex; - } - - //add debug ABC tags to main SWF - for (ABCContainerTag ds : debugSWF.getAbcList()) { - ABC a = ds.getABC(); - //Append random hex to Debugger package name - for (int i = 1; i < a.constants.getNamespaceCount(); i++) { - if (a.constants.getNamespace(i).hasName(DEBUGGER_PACKAGE, a.constants)) { - a.constants.getNamespace(i).name_index = a.constants.getStringId(newdebuggerpkg, true); - } - } - //Set debugger port to actually set port - for (int i = 0; i < a.constants.getIntCount(); i++) { - if (a.constants.getInt(i) == 123456L) { - a.constants.setInt(i, (long) port); - } - } - //Add to target SWF - ((Tag) ds).setSwf(swf); - swf.addTag((Tag) ds, (Tag) firstAbc); - swf.getAbcList().add(swf.getAbcList().indexOf(firstAbc), ds); - ((Tag) ds).setModified(true); - - //To allow socket connection to FFDec. Is this safe? - FileAttributesTag ft = swf.getFileAttributes(); - ft.useNetwork = true; - ft.setModified(true); - } - - } catch (Exception ex) { - logger.log(Level.SEVERE, "Error while attaching debugger", ex); - //ignore - } - - } - initDebugger(); - } - - public static Debugger initDebugger() { - if (debugger == null) { - synchronized (Main.class) { - if (debugger == null) { - Debugger dbg = new Debugger(Configuration.debuggerPort.get()); - dbg.start(); - debugger = dbg; - } - } - } - return debugger; - } - - public static void debuggerShowLog() { - initDebugger(); - if (Main.debugDialog == null) { - Main.debugDialog = new DebugLogDialog(debugger); - } - Main.debugDialog.setVisible(true); - } -} +/* + * Copyright (C) 2010-2016 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.debugger; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.gui.DebugLogDialog; +import com.jpexs.decompiler.flash.gui.Main; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.FileAttributesTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.helpers.Helper; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +/** + * + * @author JPEXS + */ +public class DebuggerTools { + + private static final Logger logger = Logger.getLogger(DebuggerTools.class.getName()); + + public static final String DEBUGGER_PACKAGE = "com.jpexs.decompiler.flash.debugger"; + + private static volatile Debugger debugger; + + private static ScriptPack getDebuggerScriptPack(SWF swf) { + List allAbcList = new ArrayList<>(); + for (ABCContainerTag ac : swf.getAbcList()) { + allAbcList.add(ac.getABC()); + } + for (ABCContainerTag ac : swf.getAbcList()) { + ABC a = ac.getABC(); + for (ScriptPack m : a.getScriptPacks(DEBUGGER_PACKAGE, allAbcList)) { + if (isDebuggerClass(m.getClassPath().packageStr.toRawString(), null)) { + return m; + } + } + } + return null; + } + + public static boolean hasDebugger(SWF swf) { + return getDebuggerScriptPack(swf) != null; + } + + private static boolean isDebuggerClass(String tested, String cls) { + if (tested == null) { + return false; + } + + // fast check, because dynamic regex compile and match is expensive + if (!tested.startsWith(DEBUGGER_PACKAGE)) { + return false; + } + + if (cls == null) { + cls = ""; + } else { + cls = "\\." + Pattern.quote(cls); + } + + return tested.matches(Pattern.quote(DEBUGGER_PACKAGE) + "(\\.pkg[a-f0-9]+)?" + cls); + } + + public static void injectDebugLoader(SWF swf) { + if (hasDebugger(swf)) { + ScriptPack dsp = getDebuggerScriptPack(swf); + String debuggerPkg = dsp.getClassPath().packageStr.toRawString(); + for (ABCContainerTag ct : swf.getAbcList()) { + ABC a = ct.getABC(); + if (dsp.abc == a) { //do not replace Loader in debugger itself + continue; + } + for (int i = 1; i < a.constants.getMultinameCount(); i++) { + Multiname m = a.constants.getMultiname(i); + if ("flash.display.Loader".equals(m.getNameWithNamespace(a.constants).toRawString())) { + m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true); + m.name_index = a.constants.getStringId("DebugLoader", true); + ((Tag) ct).setModified(true); + } else if ("flash.utils.getDefinitionByName".equals(m.getNameWithNamespace(a.constants).toRawString())) { + m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true); + m.name_index = a.constants.getStringId("debugGetDefinitionByName", true); + ((Tag) ct).setModified(true); + } else if ("flash.utils.getQualifiedClassName".equals(m.getNameWithNamespace(a.constants).toRawString())) { + m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true); + m.name_index = a.constants.getStringId("debugGetQualifiedClassName", true); + ((Tag) ct).setModified(true); + } else if ("flash.utils.getQualifiedSuperclassName".equals(m.getNameWithNamespace(a.constants).toRawString())) { + m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true); + m.name_index = a.constants.getStringId("debugGetQualifiedSuperclassName", true); + ((Tag) ct).setModified(true); + } else if ("flash.utils.describeType".equals(m.getNameWithNamespace(a.constants).toRawString())) { + m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true); + m.name_index = a.constants.getStringId("debugDescribeType", true); + ((Tag) ct).setModified(true); + } + } + } + } + } + + public static void replaceTraceCalls(SWF swf, String fname) { + if (hasDebugger(swf)) { + String debuggerPkg = getDebuggerScriptPack(swf).getClassPath().packageStr.toRawString(); + //change trace to fname + for (ABCContainerTag ct : swf.getAbcList()) { + ABC a = ct.getABC(); + for (int i = 1; i < a.constants.getMultinameCount(); i++) { + Multiname m = a.constants.getMultiname(i); + if ("trace".equals(m.getNameWithNamespace(a.constants).toRawString())) { + m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, debuggerPkg, 0, true); + m.name_index = a.constants.getStringId(fname, true); + ((Tag) ct).setModified(true); + } + } + } + } + } + + public static void switchDebugger(SWF swf) { + int port = Configuration.debuggerPort.get(); + ScriptPack found = getDebuggerScriptPack(swf); + if (found != null) { + ABCContainerTag tag = found.abc.parentTag; + swf.removeTag((Tag) tag); + swf.getAbcList().remove(tag); + + //Change all debugger calls to normal trace / Loader + for (ABCContainerTag ct : swf.getAbcList()) { + ABC a = ct.getABC(); + for (int i = 1; i < a.constants.getMultinameCount(); i++) { + Multiname m = a.constants.getMultiname(i); + String packageStr = m.getNameWithNamespace(a.constants).toString(); + if (isDebuggerClass(packageStr, "debugTrace") + || isDebuggerClass(packageStr, "debugAlert") + || isDebuggerClass(packageStr, "debugSocket") + || isDebuggerClass(packageStr, "debugConsole")) { + m.name_index = a.constants.getStringId("trace", true); + m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, "", 0, true); + ((Tag) ct).setModified(true); + } else if (isDebuggerClass(packageStr, "DebugLoader")) { + m.name_index = a.constants.getStringId("Loader", true); + m.namespace_index = a.constants.getNamespaceId(Namespace.KIND_PACKAGE, "flash.display", 0, true); + } + } + } + } else { + Random rnd = new Random(); + byte[] rb = new byte[16]; + rnd.nextBytes(rb); + String rhex = Helper.byteArrayToHex(rb); + try { + //load debug swf + SWF debugSWF = new SWF(Main.class.getClassLoader().getResourceAsStream("com/jpexs/decompiler/flash/gui/debugger/debug.swf"), false); + + List al = swf.getAbcList(); + ABCContainerTag firstAbc = al.isEmpty() ? null : al.get(0); + if (firstAbc == null) { //nothing to instrument? + return; + } + String newdebuggerpkg = DEBUGGER_PACKAGE; + + if (Configuration.randomDebuggerPackage.get()) { + newdebuggerpkg += ".pkg" + rhex; + } + + //add debug ABC tags to main SWF + for (ABCContainerTag ds : debugSWF.getAbcList()) { + ABC a = ds.getABC(); + //Append random hex to Debugger package name + for (int i = 1; i < a.constants.getNamespaceCount(); i++) { + if (a.constants.getNamespace(i).hasName(DEBUGGER_PACKAGE, a.constants)) { + a.constants.getNamespace(i).name_index = a.constants.getStringId(newdebuggerpkg, true); + } + } + //Set debugger port to actually set port + for (int i = 0; i < a.constants.getIntCount(); i++) { + if (a.constants.getInt(i) == 123456L) { + a.constants.setInt(i, (long) port); + } + } + //Add to target SWF + ((Tag) ds).setSwf(swf); + swf.addTag((Tag) ds, (Tag) firstAbc); + swf.getAbcList().add(swf.getAbcList().indexOf(firstAbc), ds); + ((Tag) ds).setModified(true); + + //To allow socket connection to FFDec. Is this safe? + FileAttributesTag ft = swf.getFileAttributes(); + ft.useNetwork = true; + ft.setModified(true); + } + + } catch (Exception ex) { + logger.log(Level.SEVERE, "Error while attaching debugger", ex); + //ignore + } + + } + initDebugger(); + } + + public static Debugger initDebugger() { + if (debugger == null) { + synchronized (Main.class) { + if (debugger == null) { + Debugger dbg = new Debugger(Configuration.debuggerPort.get()); + dbg.start(); + debugger = dbg; + } + } + } + return debugger; + } + + public static void debuggerShowLog() { + initDebugger(); + if (Main.debugDialog == null) { + Main.debugDialog = new DebugLogDialog(debugger); + } + Main.debugDialog.setVisible(true); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/dumpview/DumpTree.java b/src/com/jpexs/decompiler/flash/gui/dumpview/DumpTree.java index 8c3f484f3..3adbdbbc4 100644 --- a/src/com/jpexs/decompiler/flash/gui/dumpview/DumpTree.java +++ b/src/com/jpexs/decompiler/flash/gui/dumpview/DumpTree.java @@ -1,541 +1,541 @@ -/* - * Copyright (C) 2010-2016 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.dumpview; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.ABCInputStream; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.ActionListReader; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.dumpview.DumpInfo; -import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecial; -import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecialType; -import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode; -import com.jpexs.decompiler.flash.gui.Main; -import com.jpexs.decompiler.flash.gui.MainPanel; -import com.jpexs.decompiler.flash.gui.TreeNodeType; -import com.jpexs.decompiler.flash.gui.View; -import com.jpexs.decompiler.flash.gui.tagtree.TagTree; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; -import com.jpexs.decompiler.flash.tags.DefineBitsTag; -import com.jpexs.decompiler.flash.tags.DefineButton2Tag; -import com.jpexs.decompiler.flash.tags.DefineButtonTag; -import com.jpexs.decompiler.flash.tags.DefineEditTextTag; -import com.jpexs.decompiler.flash.tags.DefineFont2Tag; -import com.jpexs.decompiler.flash.tags.DefineFont3Tag; -import com.jpexs.decompiler.flash.tags.DefineFont4Tag; -import com.jpexs.decompiler.flash.tags.DefineFontTag; -import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; -import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; -import com.jpexs.decompiler.flash.tags.DefineShape2Tag; -import com.jpexs.decompiler.flash.tags.DefineShape3Tag; -import com.jpexs.decompiler.flash.tags.DefineShape4Tag; -import com.jpexs.decompiler.flash.tags.DefineShapeTag; -import com.jpexs.decompiler.flash.tags.DefineSoundTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DefineText2Tag; -import com.jpexs.decompiler.flash.tags.DefineTextTag; -import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; -import com.jpexs.decompiler.flash.tags.DoABC2Tag; -import com.jpexs.decompiler.flash.tags.DoABCTag; -import com.jpexs.decompiler.flash.tags.DoActionTag; -import com.jpexs.decompiler.flash.tags.DoInitActionTag; -import com.jpexs.decompiler.flash.tags.FileAttributesTag; -import com.jpexs.decompiler.flash.tags.MetadataTag; -import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; -import com.jpexs.decompiler.flash.tags.PlaceObject3Tag; -import com.jpexs.decompiler.flash.tags.PlaceObject4Tag; -import com.jpexs.decompiler.flash.tags.PlaceObjectTag; -import com.jpexs.decompiler.flash.tags.RemoveObject2Tag; -import com.jpexs.decompiler.flash.tags.RemoveObjectTag; -import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.SoundStreamHead2Tag; -import com.jpexs.decompiler.flash.tags.SoundStreamHeadTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.MemoryInputStream; -import java.awt.Color; -import java.awt.Component; -import java.awt.event.ActionEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; -import javax.swing.JTree; -import javax.swing.SwingUtilities; -import javax.swing.plaf.basic.BasicLabelUI; -import javax.swing.plaf.basic.BasicTreeUI; -import javax.swing.tree.DefaultTreeCellRenderer; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; - -/** - * - * @author JPEXS - */ -public class DumpTree extends JTree { - - private static final Logger logger = Logger.getLogger(DumpTree.class.getName()); - - private final MainPanel mainPanel; - - public class DumpTreeCellRenderer extends DefaultTreeCellRenderer { - - public DumpTreeCellRenderer() { - setUI(new BasicLabelUI()); - setOpaque(false); - setBackgroundNonSelectionColor(Color.white); - } - - @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { - Component ret = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); - if (ret instanceof JLabel) { - JLabel lab = (JLabel) ret; - if (value instanceof DumpInfo) { - DumpInfo di = (DumpInfo) value; - TreeNodeType nodeType = null; - if ("".equals(di.type)) { - nodeType = TreeNodeType.FLASH; - } else if ("TAG".equals(di.type)) { - String name = di.name; - if (name.contains(" ")) { - name = name.substring(0, name.indexOf(' ')).trim(); - } - switch (name) { - case DefineFontTag.NAME: - case DefineFont2Tag.NAME: - case DefineFont3Tag.NAME: - case DefineFont4Tag.NAME: - case DefineCompactedFont.NAME: - nodeType = TreeNodeType.FONT; - break; - case DefineTextTag.NAME: - case DefineText2Tag.NAME: - case DefineEditTextTag.NAME: - nodeType = TreeNodeType.TEXT; - break; - case DefineBitsTag.NAME: - case DefineBitsJPEG2Tag.NAME: - case DefineBitsJPEG3Tag.NAME: - case DefineBitsJPEG4Tag.NAME: - case DefineBitsLosslessTag.NAME: - case DefineBitsLossless2Tag.NAME: - nodeType = TreeNodeType.IMAGE; - break; - case DefineShapeTag.NAME: - case DefineShape2Tag.NAME: - case DefineShape3Tag.NAME: - case DefineShape4Tag.NAME: - nodeType = TreeNodeType.SHAPE; - break; - case DefineMorphShapeTag.NAME: - case DefineMorphShape2Tag.NAME: - nodeType = TreeNodeType.MORPH_SHAPE; - break; - case DefineSpriteTag.NAME: - nodeType = TreeNodeType.SPRITE; - break; - case DefineButtonTag.NAME: - case DefineButton2Tag.NAME: - nodeType = TreeNodeType.BUTTON; - break; - case DefineVideoStreamTag.NAME: - nodeType = TreeNodeType.MOVIE; - break; - - case DefineSoundTag.NAME: - case SoundStreamHeadTag.NAME: - case SoundStreamHead2Tag.NAME: - nodeType = TreeNodeType.SOUND; - break; - case DefineBinaryDataTag.NAME: - nodeType = TreeNodeType.BINARY_DATA; - break; - case DoActionTag.NAME: - case DoInitActionTag.NAME: - case DoABCTag.NAME: - case DoABC2Tag.NAME: - nodeType = TreeNodeType.AS; - break; - case ShowFrameTag.NAME: - nodeType = TreeNodeType.FRAME; //show_frame? - break; - case SetBackgroundColorTag.NAME: - nodeType = TreeNodeType.SET_BACKGROUNDCOLOR; - break; - case FileAttributesTag.NAME: - nodeType = TreeNodeType.FILE_ATTRIBUTES; - break; - case MetadataTag.NAME: - nodeType = TreeNodeType.METADATA; - break; - case PlaceObjectTag.NAME: - case PlaceObject2Tag.NAME: - case PlaceObject3Tag.NAME: - case PlaceObject4Tag.NAME: - nodeType = TreeNodeType.PLACE_OBJECT; - break; - case RemoveObjectTag.NAME: - case RemoveObject2Tag.NAME: - nodeType = TreeNodeType.REMOVE_OBJECT; - break; - default: - nodeType = TreeNodeType.OTHER_TAG; - } - } - if (nodeType != null) { - lab.setIcon(TagTree.getIconForType(nodeType)); - } - } - } - return ret; - - } - } - - public DumpTree(DumpTreeModel treeModel, MainPanel mainPanel) { - super(treeModel); - this.mainPanel = mainPanel; - setCellRenderer(new DumpTreeCellRenderer()); - setRootVisible(false); - setBackground(Color.white); - setUI(new BasicTreeUI() { - { - setHashColor(Color.gray); - } - }); - } - - public void createContextMenu() { - final JPopupMenu contextPopupMenu = new JPopupMenu(); - - final JMenuItem expandRecursiveMenuItem = new JMenuItem(mainPanel.translate("contextmenu.expandAll")); - expandRecursiveMenuItem.addActionListener(this::expandRecursiveButtonActionPerformed); - contextPopupMenu.add(expandRecursiveMenuItem); - - final JMenuItem saveToFileMenuItem = new JMenuItem(mainPanel.translate("contextmenu.saveToFile")); - saveToFileMenuItem.addActionListener(this::saveToFileButtonActionPerformed); - contextPopupMenu.add(saveToFileMenuItem); - - final JMenuItem saveUncompressedToFileMenuItem = new JMenuItem(mainPanel.translate("contextmenu.saveUncompressedToFile")); - saveUncompressedToFileMenuItem.addActionListener(this::saveUncompressedToFileButtonActionPerformed); - contextPopupMenu.add(saveUncompressedToFileMenuItem); - - final JMenuItem closeSelectionMenuItem = new JMenuItem(mainPanel.translate("contextmenu.closeSwf")); - closeSelectionMenuItem.addActionListener(this::closeSwfButtonActionPerformed); - contextPopupMenu.add(closeSelectionMenuItem); - - final JMenuItem parseActionsMenuItem = new JMenuItem(mainPanel.translate("contextmenu.parseActions")); - parseActionsMenuItem.addActionListener(this::parseActionsButtonActionPerformed); - contextPopupMenu.add(parseActionsMenuItem); - - final JMenuItem parseAbcMenuItem = new JMenuItem(mainPanel.translate("contextmenu.parseABC")); - parseAbcMenuItem.addActionListener(this::parseAbcButtonActionPerformed); - contextPopupMenu.add(parseAbcMenuItem); - - final JMenuItem parseInstructionsMenuItem = new JMenuItem(mainPanel.translate("contextmenu.parseInstructions")); - parseInstructionsMenuItem.addActionListener(this::parseInstructionsButtonActionPerformed); - contextPopupMenu.add(parseInstructionsMenuItem); - - final JMenuItem gotoTagMenuItem = new JMenuItem(mainPanel.translate("contextmenu.showInResources")); - gotoTagMenuItem.addActionListener(this::gotoTagButtonActionPerformed); - contextPopupMenu.add(gotoTagMenuItem); - - final JMenuItem gotoActionListMenuItem = new JMenuItem(mainPanel.translate("contextmenu.showInResources")); - gotoActionListMenuItem.addActionListener(this::gotoActionListButtonActionPerformed); - contextPopupMenu.add(gotoActionListMenuItem); - - final JMenuItem gotoMethodMenuItem = new JMenuItem(mainPanel.translate("contextmenu.showInResources")); - gotoMethodMenuItem.addActionListener(this::gotoMethodButtonActionPerformed); - contextPopupMenu.add(gotoMethodMenuItem); - - addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (SwingUtilities.isRightMouseButton(e)) { - - int row = getClosestRowForLocation(e.getX(), e.getY()); - int[] selectionRows = getSelectionRows(); - if (!Helper.contains(selectionRows, row)) { - setSelectionRow(row); - } - - TreePath[] paths = getSelectionPaths(); - if (paths == null || paths.length == 0) { - return; - } - - closeSelectionMenuItem.setVisible(false); - expandRecursiveMenuItem.setVisible(false); - saveToFileMenuItem.setVisible(false); - saveUncompressedToFileMenuItem.setVisible(false); - parseActionsMenuItem.setVisible(false); - parseAbcMenuItem.setVisible(false); - parseInstructionsMenuItem.setVisible(false); - gotoTagMenuItem.setVisible(false); - gotoActionListMenuItem.setVisible(false); - gotoMethodMenuItem.setVisible(false); - - if (paths.length == 1) { - DumpInfo treeNode = (DumpInfo) paths[0].getLastPathComponent(); - DumpInfoSpecialType specialType = getSpecialType(treeNode); - - if (treeNode instanceof DumpInfoSwfNode) { - closeSelectionMenuItem.setVisible(true); - } - - if (treeNode.getEndByte() - treeNode.startByte > 3) { - saveToFileMenuItem.setVisible(true); - if (specialType == DumpInfoSpecialType.ZLIB_DATA) { - saveUncompressedToFileMenuItem.setVisible(true); - } - } - - boolean noChild = treeNode.getChildCount() == 0; - - if (noChild) { - switch (specialType) { - case ACTION_BYTES: - parseActionsMenuItem.setVisible(true); - break; - case ABC_BYTES: - parseAbcMenuItem.setVisible(true); - break; - case ABC_CODE: - parseInstructionsMenuItem.setVisible(true); - break; - } - } - - switch (specialType) { - case TAG: - gotoTagMenuItem.setVisible(true); - break; - case ACTION_BYTES: - gotoActionListMenuItem.setVisible(true); - break; - case ABC_CODE: - case ABC_METHOD_BODY: - gotoMethodMenuItem.setVisible(true); - break; - } - - TreeModel model = getModel(); - expandRecursiveMenuItem.setVisible(model.getChildCount(treeNode) > 0); - } - - contextPopupMenu.show(e.getComponent(), e.getX(), e.getY()); - } - } - }); - } - - private DumpInfoSpecialType getSpecialType(DumpInfo dumpInfo) { - DumpInfoSpecialType specialType = dumpInfo instanceof DumpInfoSpecial - ? ((DumpInfoSpecial) dumpInfo).specialType - : DumpInfoSpecialType.NONE; - return specialType; - } - - private void expandRecursiveButtonActionPerformed(ActionEvent evt) { - TreePath path = getSelectionPath(); - if (path == null) { - return; - } - View.expandTreeNodes(this, path, true); - } - - private void saveToFileButtonActionPerformed(ActionEvent evt) { - saveToFileButtonActionPerformed(false); - } - - private void saveUncompressedToFileButtonActionPerformed(ActionEvent evt) { - saveToFileButtonActionPerformed(true); - } - - private void saveToFileButtonActionPerformed(boolean decompress) { - TreePath[] paths = getSelectionPaths(); - DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent(); - JFileChooser fc = new JFileChooser(); - String selDir = Configuration.lastOpenDir.get(); - fc.setCurrentDirectory(new File(selDir)); - JFrame f = new JFrame(); - View.setWindowIcon(f); - if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) { - File sf = Helper.fixDialogFile(fc.getSelectedFile()); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(sf))) { - byte[] data = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf().originalUncompressedData; - if (decompress) { - fos.write(SWFInputStream.uncompressByteArray(data, (int) dumpInfo.startByte, (int) (dumpInfo.getEndByte() - dumpInfo.startByte + 1))); - } else { - fos.write(data, (int) dumpInfo.startByte, (int) (dumpInfo.getEndByte() - dumpInfo.startByte + 1)); - } - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - } - - private void parseActionsButtonActionPerformed(ActionEvent evt) { - TreePath[] paths = getSelectionPaths(); - DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent(); - SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); - byte[] data = swf.originalUncompressedData; - int prevLength = (int) dumpInfo.startByte; - try { - SWFInputStream rri = new SWFInputStream(swf, data); - if (prevLength != 0) { - rri.seek(prevLength); - } - List actions = ActionListReader.getOriginalActions(rri, prevLength, (int) dumpInfo.getEndByte()); - for (Action action : actions) { - DumpInfo di = new DumpInfo(action.toString(), "Action", null, action.getAddress(), action.getTotalActionLength()); - di.parent = dumpInfo; - rri.dumpInfo = di; - rri.seek(action.getAddress()); - rri.readAction(); - dumpInfo.getChildInfos().add(di); - } - repaint(); - } catch (IOException | InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - private void parseAbcButtonActionPerformed(ActionEvent evt) { - TreePath[] paths = getSelectionPaths(); - DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent(); - SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); - byte[] data = swf.originalUncompressedData; - int prevLength = (int) dumpInfo.startByte; - try { - ABCInputStream ais = new ABCInputStream(new MemoryInputStream(data, 0, prevLength + (int) dumpInfo.lengthBytes)); - ais.seek(prevLength); - ais.dumpInfo = dumpInfo; - new ABC(ais, swf, null); - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - repaint(); - } - - private void parseInstructionsButtonActionPerformed(ActionEvent evt) { - TreePath[] paths = getSelectionPaths(); - DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent(); - SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); - byte[] data = swf.originalUncompressedData; - int prevLength = (int) dumpInfo.startByte; - try { - ABCInputStream ais = new ABCInputStream(new MemoryInputStream(data, 0, prevLength + (int) dumpInfo.lengthBytes)); - ais.seek(prevLength); - ais.dumpInfo = dumpInfo; - new AVM2Code(ais, null /*FIXME! Pass correct body!*/); - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - repaint(); - } - - private void gotoTagButtonActionPerformed(ActionEvent evt) { - TreePath[] paths = getSelectionPaths(); - DumpInfoSpecial dumpInfo = (DumpInfoSpecial) paths[0].getLastPathComponent(); - - SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); - long address = (long) (Long) dumpInfo.specialValue; - Tag foundTag = null; - for (Tag tag : swf.getTags()) { - if (tag.getOriginalRange().getPos() == address) { - foundTag = tag; - break; - } - } - - if (foundTag != null) { - mainPanel.getMainFrame().getMenu().showResourcesView(); - mainPanel.setTagTreeSelectedNode(foundTag); - } - } - - private void gotoActionListButtonActionPerformed(ActionEvent evt) { - TreePath[] paths = getSelectionPaths(); - DumpInfoSpecial dumpInfo = (DumpInfoSpecial) paths[0].getLastPathComponent(); - - SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); - long address = (long) (Long) dumpInfo.specialValue; - mainPanel.getMainFrame().getMenu().showResourcesView(); - //mainPanel.setTagTreeSelectedNode(asm); - } - - private void gotoMethodButtonActionPerformed(ActionEvent evt) { - TreePath[] paths = getSelectionPaths(); - DumpInfoSpecial dumpInfo = (DumpInfoSpecial) paths[0].getLastPathComponent(); - if (dumpInfo.specialType == DumpInfoSpecialType.ABC_CODE) { - dumpInfo = (DumpInfoSpecial) dumpInfo.parent; // method_body - } - - SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); - int method_info = (int) dumpInfo.specialValue; - // todo - } - - private void closeSwfButtonActionPerformed(ActionEvent evt) { - Main.closeFile(mainPanel.getCurrentSwfList()); - } - - @Override - public DumpTreeModel getModel() { - return (DumpTreeModel) super.getModel(); - } - - public void expandRoot() { - DumpTreeModel dtm = getModel(); - DumpInfo root = dtm.getRoot(); - expandPath(new TreePath(new Object[]{root})); - } - - public void expandFirstLevelNodes() { - DumpTreeModel dtm = getModel(); - DumpInfo root = dtm.getRoot(); - int childCount = dtm.getChildCount(root); - expandPath(new TreePath(new Object[]{root})); - for (int i = 0; i < childCount; i++) { - expandPath(new TreePath(new Object[]{root, dtm.getChild(root, i)})); - } - } -} +/* + * Copyright (C) 2010-2016 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.dumpview; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ABCInputStream; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionListReader; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.dumpview.DumpInfo; +import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecial; +import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecialType; +import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode; +import com.jpexs.decompiler.flash.gui.Main; +import com.jpexs.decompiler.flash.gui.MainPanel; +import com.jpexs.decompiler.flash.gui.TreeNodeType; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.tagtree.TagTree; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; +import com.jpexs.decompiler.flash.tags.DefineBitsTag; +import com.jpexs.decompiler.flash.tags.DefineButton2Tag; +import com.jpexs.decompiler.flash.tags.DefineButtonTag; +import com.jpexs.decompiler.flash.tags.DefineEditTextTag; +import com.jpexs.decompiler.flash.tags.DefineFont2Tag; +import com.jpexs.decompiler.flash.tags.DefineFont3Tag; +import com.jpexs.decompiler.flash.tags.DefineFont4Tag; +import com.jpexs.decompiler.flash.tags.DefineFontTag; +import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; +import com.jpexs.decompiler.flash.tags.DefineShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineShape3Tag; +import com.jpexs.decompiler.flash.tags.DefineShape4Tag; +import com.jpexs.decompiler.flash.tags.DefineShapeTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DefineText2Tag; +import com.jpexs.decompiler.flash.tags.DefineTextTag; +import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; +import com.jpexs.decompiler.flash.tags.DoABC2Tag; +import com.jpexs.decompiler.flash.tags.DoABCTag; +import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import com.jpexs.decompiler.flash.tags.FileAttributesTag; +import com.jpexs.decompiler.flash.tags.MetadataTag; +import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; +import com.jpexs.decompiler.flash.tags.PlaceObject3Tag; +import com.jpexs.decompiler.flash.tags.PlaceObject4Tag; +import com.jpexs.decompiler.flash.tags.PlaceObjectTag; +import com.jpexs.decompiler.flash.tags.RemoveObject2Tag; +import com.jpexs.decompiler.flash.tags.RemoveObjectTag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SoundStreamHead2Tag; +import com.jpexs.decompiler.flash.tags.SoundStreamHeadTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.MemoryInputStream; +import java.awt.Color; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.JTree; +import javax.swing.SwingUtilities; +import javax.swing.plaf.basic.BasicLabelUI; +import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +/** + * + * @author JPEXS + */ +public class DumpTree extends JTree { + + private static final Logger logger = Logger.getLogger(DumpTree.class.getName()); + + private final MainPanel mainPanel; + + public class DumpTreeCellRenderer extends DefaultTreeCellRenderer { + + public DumpTreeCellRenderer() { + setUI(new BasicLabelUI()); + setOpaque(false); + setBackgroundNonSelectionColor(Color.white); + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { + Component ret = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + if (ret instanceof JLabel) { + JLabel lab = (JLabel) ret; + if (value instanceof DumpInfo) { + DumpInfo di = (DumpInfo) value; + TreeNodeType nodeType = null; + if ("".equals(di.type)) { + nodeType = TreeNodeType.FLASH; + } else if ("TAG".equals(di.type)) { + String name = di.name; + if (name.contains(" ")) { + name = name.substring(0, name.indexOf(' ')).trim(); + } + switch (name) { + case DefineFontTag.NAME: + case DefineFont2Tag.NAME: + case DefineFont3Tag.NAME: + case DefineFont4Tag.NAME: + case DefineCompactedFont.NAME: + nodeType = TreeNodeType.FONT; + break; + case DefineTextTag.NAME: + case DefineText2Tag.NAME: + case DefineEditTextTag.NAME: + nodeType = TreeNodeType.TEXT; + break; + case DefineBitsTag.NAME: + case DefineBitsJPEG2Tag.NAME: + case DefineBitsJPEG3Tag.NAME: + case DefineBitsJPEG4Tag.NAME: + case DefineBitsLosslessTag.NAME: + case DefineBitsLossless2Tag.NAME: + nodeType = TreeNodeType.IMAGE; + break; + case DefineShapeTag.NAME: + case DefineShape2Tag.NAME: + case DefineShape3Tag.NAME: + case DefineShape4Tag.NAME: + nodeType = TreeNodeType.SHAPE; + break; + case DefineMorphShapeTag.NAME: + case DefineMorphShape2Tag.NAME: + nodeType = TreeNodeType.MORPH_SHAPE; + break; + case DefineSpriteTag.NAME: + nodeType = TreeNodeType.SPRITE; + break; + case DefineButtonTag.NAME: + case DefineButton2Tag.NAME: + nodeType = TreeNodeType.BUTTON; + break; + case DefineVideoStreamTag.NAME: + nodeType = TreeNodeType.MOVIE; + break; + + case DefineSoundTag.NAME: + case SoundStreamHeadTag.NAME: + case SoundStreamHead2Tag.NAME: + nodeType = TreeNodeType.SOUND; + break; + case DefineBinaryDataTag.NAME: + nodeType = TreeNodeType.BINARY_DATA; + break; + case DoActionTag.NAME: + case DoInitActionTag.NAME: + case DoABCTag.NAME: + case DoABC2Tag.NAME: + nodeType = TreeNodeType.AS; + break; + case ShowFrameTag.NAME: + nodeType = TreeNodeType.FRAME; //show_frame? + break; + case SetBackgroundColorTag.NAME: + nodeType = TreeNodeType.SET_BACKGROUNDCOLOR; + break; + case FileAttributesTag.NAME: + nodeType = TreeNodeType.FILE_ATTRIBUTES; + break; + case MetadataTag.NAME: + nodeType = TreeNodeType.METADATA; + break; + case PlaceObjectTag.NAME: + case PlaceObject2Tag.NAME: + case PlaceObject3Tag.NAME: + case PlaceObject4Tag.NAME: + nodeType = TreeNodeType.PLACE_OBJECT; + break; + case RemoveObjectTag.NAME: + case RemoveObject2Tag.NAME: + nodeType = TreeNodeType.REMOVE_OBJECT; + break; + default: + nodeType = TreeNodeType.OTHER_TAG; + } + } + if (nodeType != null) { + lab.setIcon(TagTree.getIconForType(nodeType)); + } + } + } + return ret; + + } + } + + public DumpTree(DumpTreeModel treeModel, MainPanel mainPanel) { + super(treeModel); + this.mainPanel = mainPanel; + setCellRenderer(new DumpTreeCellRenderer()); + setRootVisible(false); + setBackground(Color.white); + setUI(new BasicTreeUI() { + { + setHashColor(Color.gray); + } + }); + } + + public void createContextMenu() { + final JPopupMenu contextPopupMenu = new JPopupMenu(); + + final JMenuItem expandRecursiveMenuItem = new JMenuItem(mainPanel.translate("contextmenu.expandAll")); + expandRecursiveMenuItem.addActionListener(this::expandRecursiveButtonActionPerformed); + contextPopupMenu.add(expandRecursiveMenuItem); + + final JMenuItem saveToFileMenuItem = new JMenuItem(mainPanel.translate("contextmenu.saveToFile")); + saveToFileMenuItem.addActionListener(this::saveToFileButtonActionPerformed); + contextPopupMenu.add(saveToFileMenuItem); + + final JMenuItem saveUncompressedToFileMenuItem = new JMenuItem(mainPanel.translate("contextmenu.saveUncompressedToFile")); + saveUncompressedToFileMenuItem.addActionListener(this::saveUncompressedToFileButtonActionPerformed); + contextPopupMenu.add(saveUncompressedToFileMenuItem); + + final JMenuItem closeSelectionMenuItem = new JMenuItem(mainPanel.translate("contextmenu.closeSwf")); + closeSelectionMenuItem.addActionListener(this::closeSwfButtonActionPerformed); + contextPopupMenu.add(closeSelectionMenuItem); + + final JMenuItem parseActionsMenuItem = new JMenuItem(mainPanel.translate("contextmenu.parseActions")); + parseActionsMenuItem.addActionListener(this::parseActionsButtonActionPerformed); + contextPopupMenu.add(parseActionsMenuItem); + + final JMenuItem parseAbcMenuItem = new JMenuItem(mainPanel.translate("contextmenu.parseABC")); + parseAbcMenuItem.addActionListener(this::parseAbcButtonActionPerformed); + contextPopupMenu.add(parseAbcMenuItem); + + final JMenuItem parseInstructionsMenuItem = new JMenuItem(mainPanel.translate("contextmenu.parseInstructions")); + parseInstructionsMenuItem.addActionListener(this::parseInstructionsButtonActionPerformed); + contextPopupMenu.add(parseInstructionsMenuItem); + + final JMenuItem gotoTagMenuItem = new JMenuItem(mainPanel.translate("contextmenu.showInResources")); + gotoTagMenuItem.addActionListener(this::gotoTagButtonActionPerformed); + contextPopupMenu.add(gotoTagMenuItem); + + final JMenuItem gotoActionListMenuItem = new JMenuItem(mainPanel.translate("contextmenu.showInResources")); + gotoActionListMenuItem.addActionListener(this::gotoActionListButtonActionPerformed); + contextPopupMenu.add(gotoActionListMenuItem); + + final JMenuItem gotoMethodMenuItem = new JMenuItem(mainPanel.translate("contextmenu.showInResources")); + gotoMethodMenuItem.addActionListener(this::gotoMethodButtonActionPerformed); + contextPopupMenu.add(gotoMethodMenuItem); + + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (SwingUtilities.isRightMouseButton(e)) { + + int row = getClosestRowForLocation(e.getX(), e.getY()); + int[] selectionRows = getSelectionRows(); + if (!Helper.contains(selectionRows, row)) { + setSelectionRow(row); + } + + TreePath[] paths = getSelectionPaths(); + if (paths == null || paths.length == 0) { + return; + } + + closeSelectionMenuItem.setVisible(false); + expandRecursiveMenuItem.setVisible(false); + saveToFileMenuItem.setVisible(false); + saveUncompressedToFileMenuItem.setVisible(false); + parseActionsMenuItem.setVisible(false); + parseAbcMenuItem.setVisible(false); + parseInstructionsMenuItem.setVisible(false); + gotoTagMenuItem.setVisible(false); + gotoActionListMenuItem.setVisible(false); + gotoMethodMenuItem.setVisible(false); + + if (paths.length == 1) { + DumpInfo treeNode = (DumpInfo) paths[0].getLastPathComponent(); + DumpInfoSpecialType specialType = getSpecialType(treeNode); + + if (treeNode instanceof DumpInfoSwfNode) { + closeSelectionMenuItem.setVisible(true); + } + + if (treeNode.getEndByte() - treeNode.startByte > 3) { + saveToFileMenuItem.setVisible(true); + if (specialType == DumpInfoSpecialType.ZLIB_DATA) { + saveUncompressedToFileMenuItem.setVisible(true); + } + } + + boolean noChild = treeNode.getChildCount() == 0; + + if (noChild) { + switch (specialType) { + case ACTION_BYTES: + parseActionsMenuItem.setVisible(true); + break; + case ABC_BYTES: + parseAbcMenuItem.setVisible(true); + break; + case ABC_CODE: + parseInstructionsMenuItem.setVisible(true); + break; + } + } + + switch (specialType) { + case TAG: + gotoTagMenuItem.setVisible(true); + break; + case ACTION_BYTES: + gotoActionListMenuItem.setVisible(true); + break; + case ABC_CODE: + case ABC_METHOD_BODY: + gotoMethodMenuItem.setVisible(true); + break; + } + + TreeModel model = getModel(); + expandRecursiveMenuItem.setVisible(model.getChildCount(treeNode) > 0); + } + + contextPopupMenu.show(e.getComponent(), e.getX(), e.getY()); + } + } + }); + } + + private DumpInfoSpecialType getSpecialType(DumpInfo dumpInfo) { + DumpInfoSpecialType specialType = dumpInfo instanceof DumpInfoSpecial + ? ((DumpInfoSpecial) dumpInfo).specialType + : DumpInfoSpecialType.NONE; + return specialType; + } + + private void expandRecursiveButtonActionPerformed(ActionEvent evt) { + TreePath path = getSelectionPath(); + if (path == null) { + return; + } + View.expandTreeNodes(this, path, true); + } + + private void saveToFileButtonActionPerformed(ActionEvent evt) { + saveToFileButtonActionPerformed(false); + } + + private void saveUncompressedToFileButtonActionPerformed(ActionEvent evt) { + saveToFileButtonActionPerformed(true); + } + + private void saveToFileButtonActionPerformed(boolean decompress) { + TreePath[] paths = getSelectionPaths(); + DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent(); + JFileChooser fc = new JFileChooser(); + String selDir = Configuration.lastOpenDir.get(); + fc.setCurrentDirectory(new File(selDir)); + JFrame f = new JFrame(); + View.setWindowIcon(f); + if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) { + File sf = Helper.fixDialogFile(fc.getSelectedFile()); + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(sf))) { + byte[] data = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf().originalUncompressedData; + if (decompress) { + fos.write(SWFInputStream.uncompressByteArray(data, (int) dumpInfo.startByte, (int) (dumpInfo.getEndByte() - dumpInfo.startByte + 1))); + } else { + fos.write(data, (int) dumpInfo.startByte, (int) (dumpInfo.getEndByte() - dumpInfo.startByte + 1)); + } + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + + private void parseActionsButtonActionPerformed(ActionEvent evt) { + TreePath[] paths = getSelectionPaths(); + DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent(); + SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); + byte[] data = swf.originalUncompressedData; + int prevLength = (int) dumpInfo.startByte; + try { + SWFInputStream rri = new SWFInputStream(swf, data); + if (prevLength != 0) { + rri.seek(prevLength); + } + List actions = ActionListReader.getOriginalActions(rri, prevLength, (int) dumpInfo.getEndByte()); + for (Action action : actions) { + DumpInfo di = new DumpInfo(action.toString(), "Action", null, action.getAddress(), action.getTotalActionLength()); + di.parent = dumpInfo; + rri.dumpInfo = di; + rri.seek(action.getAddress()); + rri.readAction(); + dumpInfo.getChildInfos().add(di); + } + repaint(); + } catch (IOException | InterruptedException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + private void parseAbcButtonActionPerformed(ActionEvent evt) { + TreePath[] paths = getSelectionPaths(); + DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent(); + SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); + byte[] data = swf.originalUncompressedData; + int prevLength = (int) dumpInfo.startByte; + try { + ABCInputStream ais = new ABCInputStream(new MemoryInputStream(data, 0, prevLength + (int) dumpInfo.lengthBytes)); + ais.seek(prevLength); + ais.dumpInfo = dumpInfo; + new ABC(ais, swf, null); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + repaint(); + } + + private void parseInstructionsButtonActionPerformed(ActionEvent evt) { + TreePath[] paths = getSelectionPaths(); + DumpInfo dumpInfo = (DumpInfo) paths[0].getLastPathComponent(); + SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); + byte[] data = swf.originalUncompressedData; + int prevLength = (int) dumpInfo.startByte; + try { + ABCInputStream ais = new ABCInputStream(new MemoryInputStream(data, 0, prevLength + (int) dumpInfo.lengthBytes)); + ais.seek(prevLength); + ais.dumpInfo = dumpInfo; + new AVM2Code(ais, null /*FIXME! Pass correct body!*/); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + repaint(); + } + + private void gotoTagButtonActionPerformed(ActionEvent evt) { + TreePath[] paths = getSelectionPaths(); + DumpInfoSpecial dumpInfo = (DumpInfoSpecial) paths[0].getLastPathComponent(); + + SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); + long address = (long) (Long) dumpInfo.specialValue; + Tag foundTag = null; + for (Tag tag : swf.getTags()) { + if (tag.getOriginalRange().getPos() == address) { + foundTag = tag; + break; + } + } + + if (foundTag != null) { + mainPanel.getMainFrame().getMenu().showResourcesView(); + mainPanel.setTagTreeSelectedNode(foundTag); + } + } + + private void gotoActionListButtonActionPerformed(ActionEvent evt) { + TreePath[] paths = getSelectionPaths(); + DumpInfoSpecial dumpInfo = (DumpInfoSpecial) paths[0].getLastPathComponent(); + + SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); + long address = (long) (Long) dumpInfo.specialValue; + mainPanel.getMainFrame().getMenu().showResourcesView(); + //mainPanel.setTagTreeSelectedNode(asm); + } + + private void gotoMethodButtonActionPerformed(ActionEvent evt) { + TreePath[] paths = getSelectionPaths(); + DumpInfoSpecial dumpInfo = (DumpInfoSpecial) paths[0].getLastPathComponent(); + if (dumpInfo.specialType == DumpInfoSpecialType.ABC_CODE) { + dumpInfo = (DumpInfoSpecial) dumpInfo.parent; // method_body + } + + SWF swf = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); + int method_info = (int) dumpInfo.specialValue; + // todo + } + + private void closeSwfButtonActionPerformed(ActionEvent evt) { + Main.closeFile(mainPanel.getCurrentSwfList()); + } + + @Override + public DumpTreeModel getModel() { + return (DumpTreeModel) super.getModel(); + } + + public void expandRoot() { + DumpTreeModel dtm = getModel(); + DumpInfo root = dtm.getRoot(); + expandPath(new TreePath(new Object[]{root})); + } + + public void expandFirstLevelNodes() { + DumpTreeModel dtm = getModel(); + DumpInfo root = dtm.getRoot(); + int childCount = dtm.getChildCount(root); + expandPath(new TreePath(new Object[]{root})); + for (int i = 0; i < childCount; i++) { + expandPath(new TreePath(new Object[]{root, dtm.getChild(root, i)})); + } + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/dumpview/DumpViewPanel.java b/src/com/jpexs/decompiler/flash/gui/dumpview/DumpViewPanel.java index 7ff683d89..4610f45a6 100644 --- a/src/com/jpexs/decompiler/flash/gui/dumpview/DumpViewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/dumpview/DumpViewPanel.java @@ -1,360 +1,360 @@ -/* - * Copyright (C) 2010-2016 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.dumpview; - -import com.jpexs.decompiler.flash.dumpview.DumpInfo; -import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode; -import com.jpexs.decompiler.flash.gui.MyTextField; -import com.jpexs.decompiler.flash.gui.View; -import com.jpexs.decompiler.flash.gui.hexview.HexView; -import com.jpexs.decompiler.flash.gui.hexview.HexViewListener; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.utf8.Utf8Helper; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.List; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; - -/** - * - * @author JPEXS - */ -public class DumpViewPanel extends JPanel { - - private final JLabel selectedByteInfo; - - private final JLabel dumpViewLabel; - - private final HexView dumpViewHexTable; - - private JTextField filterField = new MyTextField(""); - - private JPanel searchPanel; - - private final DumpTree dumpTree; - - private DumpInfo selectedDumpInfo; - - private boolean skipNextScroll; - - private boolean skipValueChange; - - public DumpViewPanel(final DumpTree dumpTree) { - super(new BorderLayout()); - - this.dumpTree = dumpTree; - - selectedByteInfo = new JLabel(); - selectedByteInfo.setMinimumSize(new Dimension(100, 20)); - selectedByteInfo.setText("-"); - add(selectedByteInfo, BorderLayout.NORTH); - - dumpViewLabel = new JLabel(); - dumpViewLabel.setMinimumSize(new Dimension(100, 20)); - dumpViewLabel.setText("-"); - add(dumpViewLabel, BorderLayout.SOUTH); - - dumpViewHexTable = new HexView(); - dumpViewHexTable.addListener(new HexViewListener() { - private int lastAddressUnderCursor = -1; - - @Override - public void byteValueChanged(int address, byte b) { - if (skipValueChange) { - return; - } - - if (address != -1) { - TreeModel model = dumpTree.getModel(); - DumpInfo di = DumpInfoSwfNode.getSwfNode(selectedDumpInfo); - while (model.getChildCount(di) > 0) { - boolean found = false; - for (DumpInfo child : di.getChildInfos()) { - if (child.startByte > address) { - break; - } - if (child.getEndByte() >= address) { - di = child; - found = true; - } - } - if (!found) { - break; - } - } - List path = new ArrayList<>(); - while (di != null) { - path.add(0, di); - di = di.parent; - } - path.add(0, model.getRoot()); - TreePath tp = new TreePath(path.toArray()); - skipNextScroll = true; - dumpTree.setSelectionPath(tp); - dumpTree.scrollPathToVisible(tp); - } - - byte[] data = dumpViewHexTable.getData(); - byteMouseMoved(lastAddressUnderCursor, lastAddressUnderCursor == -1 ? 0 : data[lastAddressUnderCursor]); - } - - @Override - public void byteMouseMoved(int address, byte b) { - lastAddressUnderCursor = address; - if (address == -1) { - address = dumpViewHexTable.getFocusedByteIdx(); - if (address != -1) { - byte[] data = dumpViewHexTable.getData(); - b = data[address]; - } - } - - if (address != -1) { - int b2 = b & 0xff; - selectedByteInfo.setText("Addr: " + String.format("%08X", address) - + " Hex: " + String.format("%02X", b) - + " Dec: " + b2 - + " Bin: " + Helper.padZeros(Integer.toBinaryString(b2), 8) - + " Ascii: " + (char) b2 - ); - } else { - selectedByteInfo.setText("-"); - } - } - }); - - 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) { - closeDumpViewSearch(); - } - }); - searchPanel.add(closeSearchButton, BorderLayout.EAST); - searchPanel.setVisible(false); - - dumpViewHexTable.addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(KeyEvent e) { - if ((e.getKeyCode() == 'F') && (e.isControlDown())) { - searchPanel.setVisible(true); - filterField.requestFocusInWindow(); - } - } - }); - - 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() { - doSearch(); - } - }); - - JPanel hexPanel = new JPanel(new BorderLayout()); - hexPanel.add(new JScrollPane(dumpViewHexTable), BorderLayout.CENTER); - hexPanel.add(searchPanel, BorderLayout.SOUTH); - add(hexPanel, BorderLayout.CENTER); - } - - public void closeDumpViewSearch() { - filterField.setText(""); - doSearch(); - searchPanel.setVisible(false); - } - - private void doSearch() { - filterField.setBackground(Color.white); - - String text = filterField.getText(); - if (text.length() == 0) { - dumpViewHexTable.clearSelectedBytes(); - return; - } - - byte[] data = dumpViewHexTable.getData(); - - byte[] textBytes = Utf8Helper.getBytes(text); - byte[] hex = getAsHex(text); - byte[] foundArray = textBytes; - - int pos = textBytes == null ? -1 : findHex(data, textBytes, 0, data.length); - int hexPos = hex == null ? -1 : findHex(data, hex, 0, data.length); - - if (pos == -1 || (hexPos != -1 && hexPos < pos)) { - pos = hexPos; - foundArray = hex; - } - - if (pos != -1) { - dumpViewHexTable.selectBytes(pos, foundArray.length); - } else { - dumpViewHexTable.clearSelectedBytes(); - filterField.setBackground(Color.red); - } - } - - private int findHex(byte[] data, byte[] searchData, int from, int to) { - for (int i = from; i < to; i++) { - if (isMatch(data, searchData, i)) { - return i; - } - } - - return -1; - } - - private boolean isMatch(byte[] data, byte[] searchData, int pos) { - if (pos + searchData.length > data.length) { - return false; - } - - for (int i = 0; i < searchData.length; i++) { - if (data[pos + i] != searchData[i]) { - return false; - } - } - - return true; - } - - private byte[] getAsHex(String text) { - int charCount = 0; - for (int i = 0; i < text.length(); i++) { - char ch = Character.toUpperCase(text.charAt(i)); - boolean whiteSpace = Character.isWhitespace(ch); - if (!((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || whiteSpace)) { - return null; - } - - if (!whiteSpace) { - charCount++; - } - } - - if (charCount % 2 == 1) { - // hex character count should be even - return null; - } - - byte[] result = new byte[charCount / 2]; - int cnt = 0; - int v0 = 0; - for (int i = 0; i < text.length(); i++) { - char ch = Character.toUpperCase(text.charAt(i)); - if (Character.isWhitespace(ch)) { - continue; - } - - int v = Integer.parseInt(Character.toString(ch), 16); - - if (cnt % 2 == 1) { - result[cnt / 2] = (byte) (v0 * 16 + v); - } else { - v0 = v; - } - - cnt++; - } - - return result; - } - - public void clear() { - selectedDumpInfo = null; - } - - public void setSelectedNode(DumpInfo dumpInfo) { - if (this.selectedDumpInfo == dumpInfo) { - skipNextScroll = false; - return; - } - - this.selectedDumpInfo = dumpInfo; - byte[] data = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf().originalUncompressedData; - List dumpInfos = new ArrayList<>(); - DumpInfo di = dumpInfo; - while (di.parent != null) { - dumpInfos.add(di); - di = di.parent; - } - long[] highlightStarts = new long[dumpInfos.size()]; - long[] highlightEnds = new long[dumpInfos.size()]; - for (int i = 0; i < dumpInfos.size(); i++) { - DumpInfo di2 = dumpInfos.get(highlightStarts.length - i - 1); - highlightStarts[i] = di2.startByte; - highlightEnds[i] = di2.getEndByte(); - } - dumpViewHexTable.setData(data, highlightStarts, highlightEnds); - dumpViewHexTable.revalidate(); - - if (dumpInfo.lengthBytes != 0 || dumpInfo.lengthBits != 0) { - int selectionStart = (int) dumpInfo.startByte; - int selectionEnd = (int) dumpInfo.getEndByte(); - - if (!skipNextScroll) { - skipValueChange = true; - dumpViewHexTable.scrollToByte(highlightStarts, highlightEnds); - skipValueChange = false; - } - - setLabelText("startByte: " + dumpInfo.startByte - + " startBit: " + dumpInfo.startBit - + " lengthBytes: " + dumpInfo.lengthBytes - + " lengthBits: " + dumpInfo.lengthBits - + " selectionStart: " + selectionStart - + " selectionEnd: " + selectionEnd); - } - - skipNextScroll = false; - repaint(); - } - - public void setLabelText(String text) { - dumpViewLabel.setText(text); - } -} +/* + * Copyright (C) 2010-2016 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.dumpview; + +import com.jpexs.decompiler.flash.dumpview.DumpInfo; +import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode; +import com.jpexs.decompiler.flash.gui.MyTextField; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.hexview.HexView; +import com.jpexs.decompiler.flash.gui.hexview.HexViewListener; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.utf8.Utf8Helper; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +/** + * + * @author JPEXS + */ +public class DumpViewPanel extends JPanel { + + private final JLabel selectedByteInfo; + + private final JLabel dumpViewLabel; + + private final HexView dumpViewHexTable; + + private JTextField filterField = new MyTextField(""); + + private JPanel searchPanel; + + private final DumpTree dumpTree; + + private DumpInfo selectedDumpInfo; + + private boolean skipNextScroll; + + private boolean skipValueChange; + + public DumpViewPanel(final DumpTree dumpTree) { + super(new BorderLayout()); + + this.dumpTree = dumpTree; + + selectedByteInfo = new JLabel(); + selectedByteInfo.setMinimumSize(new Dimension(100, 20)); + selectedByteInfo.setText("-"); + add(selectedByteInfo, BorderLayout.NORTH); + + dumpViewLabel = new JLabel(); + dumpViewLabel.setMinimumSize(new Dimension(100, 20)); + dumpViewLabel.setText("-"); + add(dumpViewLabel, BorderLayout.SOUTH); + + dumpViewHexTable = new HexView(); + dumpViewHexTable.addListener(new HexViewListener() { + private int lastAddressUnderCursor = -1; + + @Override + public void byteValueChanged(int address, byte b) { + if (skipValueChange) { + return; + } + + if (address != -1) { + TreeModel model = dumpTree.getModel(); + DumpInfo di = DumpInfoSwfNode.getSwfNode(selectedDumpInfo); + while (model.getChildCount(di) > 0) { + boolean found = false; + for (DumpInfo child : di.getChildInfos()) { + if (child.startByte > address) { + break; + } + if (child.getEndByte() >= address) { + di = child; + found = true; + } + } + if (!found) { + break; + } + } + List path = new ArrayList<>(); + while (di != null) { + path.add(0, di); + di = di.parent; + } + path.add(0, model.getRoot()); + TreePath tp = new TreePath(path.toArray()); + skipNextScroll = true; + dumpTree.setSelectionPath(tp); + dumpTree.scrollPathToVisible(tp); + } + + byte[] data = dumpViewHexTable.getData(); + byteMouseMoved(lastAddressUnderCursor, lastAddressUnderCursor == -1 ? 0 : data[lastAddressUnderCursor]); + } + + @Override + public void byteMouseMoved(int address, byte b) { + lastAddressUnderCursor = address; + if (address == -1) { + address = dumpViewHexTable.getFocusedByteIdx(); + if (address != -1) { + byte[] data = dumpViewHexTable.getData(); + b = data[address]; + } + } + + if (address != -1) { + int b2 = b & 0xff; + selectedByteInfo.setText("Addr: " + String.format("%08X", address) + + " Hex: " + String.format("%02X", b) + + " Dec: " + b2 + + " Bin: " + Helper.padZeros(Integer.toBinaryString(b2), 8) + + " Ascii: " + (char) b2 + ); + } else { + selectedByteInfo.setText("-"); + } + } + }); + + 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) { + closeDumpViewSearch(); + } + }); + searchPanel.add(closeSearchButton, BorderLayout.EAST); + searchPanel.setVisible(false); + + dumpViewHexTable.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if ((e.getKeyCode() == 'F') && (e.isControlDown())) { + searchPanel.setVisible(true); + filterField.requestFocusInWindow(); + } + } + }); + + 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() { + doSearch(); + } + }); + + JPanel hexPanel = new JPanel(new BorderLayout()); + hexPanel.add(new JScrollPane(dumpViewHexTable), BorderLayout.CENTER); + hexPanel.add(searchPanel, BorderLayout.SOUTH); + add(hexPanel, BorderLayout.CENTER); + } + + public void closeDumpViewSearch() { + filterField.setText(""); + doSearch(); + searchPanel.setVisible(false); + } + + private void doSearch() { + filterField.setBackground(Color.white); + + String text = filterField.getText(); + if (text.length() == 0) { + dumpViewHexTable.clearSelectedBytes(); + return; + } + + byte[] data = dumpViewHexTable.getData(); + + byte[] textBytes = Utf8Helper.getBytes(text); + byte[] hex = getAsHex(text); + byte[] foundArray = textBytes; + + int pos = textBytes == null ? -1 : findHex(data, textBytes, 0, data.length); + int hexPos = hex == null ? -1 : findHex(data, hex, 0, data.length); + + if (pos == -1 || (hexPos != -1 && hexPos < pos)) { + pos = hexPos; + foundArray = hex; + } + + if (pos != -1) { + dumpViewHexTable.selectBytes(pos, foundArray.length); + } else { + dumpViewHexTable.clearSelectedBytes(); + filterField.setBackground(Color.red); + } + } + + private int findHex(byte[] data, byte[] searchData, int from, int to) { + for (int i = from; i < to; i++) { + if (isMatch(data, searchData, i)) { + return i; + } + } + + return -1; + } + + private boolean isMatch(byte[] data, byte[] searchData, int pos) { + if (pos + searchData.length > data.length) { + return false; + } + + for (int i = 0; i < searchData.length; i++) { + if (data[pos + i] != searchData[i]) { + return false; + } + } + + return true; + } + + private byte[] getAsHex(String text) { + int charCount = 0; + for (int i = 0; i < text.length(); i++) { + char ch = Character.toUpperCase(text.charAt(i)); + boolean whiteSpace = Character.isWhitespace(ch); + if (!((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || whiteSpace)) { + return null; + } + + if (!whiteSpace) { + charCount++; + } + } + + if (charCount % 2 == 1) { + // hex character count should be even + return null; + } + + byte[] result = new byte[charCount / 2]; + int cnt = 0; + int v0 = 0; + for (int i = 0; i < text.length(); i++) { + char ch = Character.toUpperCase(text.charAt(i)); + if (Character.isWhitespace(ch)) { + continue; + } + + int v = Integer.parseInt(Character.toString(ch), 16); + + if (cnt % 2 == 1) { + result[cnt / 2] = (byte) (v0 * 16 + v); + } else { + v0 = v; + } + + cnt++; + } + + return result; + } + + public void clear() { + selectedDumpInfo = null; + } + + public void setSelectedNode(DumpInfo dumpInfo) { + if (this.selectedDumpInfo == dumpInfo) { + skipNextScroll = false; + return; + } + + this.selectedDumpInfo = dumpInfo; + byte[] data = DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf().originalUncompressedData; + List dumpInfos = new ArrayList<>(); + DumpInfo di = dumpInfo; + while (di.parent != null) { + dumpInfos.add(di); + di = di.parent; + } + long[] highlightStarts = new long[dumpInfos.size()]; + long[] highlightEnds = new long[dumpInfos.size()]; + for (int i = 0; i < dumpInfos.size(); i++) { + DumpInfo di2 = dumpInfos.get(highlightStarts.length - i - 1); + highlightStarts[i] = di2.startByte; + highlightEnds[i] = di2.getEndByte(); + } + dumpViewHexTable.setData(data, highlightStarts, highlightEnds); + dumpViewHexTable.revalidate(); + + if (dumpInfo.lengthBytes != 0 || dumpInfo.lengthBits != 0) { + int selectionStart = (int) dumpInfo.startByte; + int selectionEnd = (int) dumpInfo.getEndByte(); + + if (!skipNextScroll) { + skipValueChange = true; + dumpViewHexTable.scrollToByte(highlightStarts, highlightEnds); + skipValueChange = false; + } + + setLabelText("startByte: " + dumpInfo.startByte + + " startBit: " + dumpInfo.startBit + + " lengthBytes: " + dumpInfo.lengthBytes + + " lengthBits: " + dumpInfo.lengthBits + + " selectionStart: " + selectionStart + + " selectionEnd: " + selectionEnd); + } + + skipNextScroll = false; + repaint(); + } + + public void setLabelText(String text) { + dumpViewLabel.setText(text); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/hexview/HexView.java b/src/com/jpexs/decompiler/flash/gui/hexview/HexView.java index 5ec5cdf87..8484df926 100644 --- a/src/com/jpexs/decompiler/flash/gui/hexview/HexView.java +++ b/src/com/jpexs/decompiler/flash/gui/hexview/HexView.java @@ -1,296 +1,296 @@ -/* - * Copyright (C) 2010-2016 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.hexview; - -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseMotionAdapter; -import javax.swing.JLabel; -import javax.swing.JTable; -import javax.swing.ListSelectionModel; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.JTableHeader; -import javax.swing.table.TableColumn; -import javax.swing.table.TableModel; - -/** - * - * @author JPEXS - */ -public class HexView extends JTable { - - private static final int bytesInRow = 16; - - private long[] highlightStarts; - - private long[] highlightEnds; - - private final String[] highlightColorsStr = new String[]{/*"EEEEEE", */"29AEC2", "9AC88C", "DF5F80", "EEA32E", "FFD200", "5E9B4C", "D3E976", "A3AEC2"}; - - private final Color[] highlightColors; - - private final Color bgColor = Color.decode("#F7F7F7"); - - private final Color bgColorAlternate = Color.decode("#EDEDED"); - - private int mouseOverIdx = -1; - - private int selectionStart = -1; - - private int selectionEnd = -1; - - private HexViewListener listener; - - private class HighlightCellRenderer extends DefaultTableCellRenderer { - - public int byteIndex; - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) { - - JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col); - int level = -1; - int idx = getIdxByColAndRow(row, col); - if (highlightStarts != null) { - byteIndex = idx; - for (int i = 0; i < highlightStarts.length; i++) { - if (highlightStarts[i] <= idx && highlightEnds[i] >= idx) { - level++; - } else { - break; - } - } - } - - Color foreground; - Color background; - if (level > -1) { - foreground = Color.white; - background = highlightColors[level % highlightColors.length]; - } else { - foreground = Color.black; - background = row % 2 == 0 ? bgColor : bgColorAlternate; - } - - if (idx != -1 && (idx == mouseOverIdx - || (idx >= selectionStart && idx <= selectionEnd))) { - foreground = new Color(255 - foreground.getRed(), 255 - foreground.getGreen(), 255 - foreground.getBlue()); - background = new Color(255 - background.getRed(), 255 - background.getGreen(), 255 - background.getBlue()); - } - - l.setForeground(foreground); - l.setBackground(background); - - return l; - } - } - - private class HexViewSelectionListener implements ListSelectionListener { - - private final HexView table; - - public HexViewSelectionListener(HexView table) { - this.table = table; - } - - @Override - public void valueChanged(ListSelectionEvent e) { - - int col = table.getSelectedColumn(); - int row = table.getSelectedRow(); - - int idx = getIdxByColAndRow(row, col); - if (listener != null) { - listener.byteValueChanged(idx, idx == -1 ? 0 : getModel().getData()[idx]); - } - } - } - - private class HexViewMouseAdapter extends MouseAdapter { - - @Override - public void mouseExited(MouseEvent e) { - HexView table = (HexView) e.getSource(); - Point point = e.getPoint(); - int col = table.columnAtPoint(point); - int row = table.rowAtPoint(point); - mouseOverIdx = -1; - getModel().fireTableCellUpdated(row, col); - if (listener != null) { - listener.byteMouseMoved(-1, (byte) 0); - } - } - } - - private class HexViewMouseMotionAdapter extends MouseMotionAdapter { - - @Override - public void mouseMoved(MouseEvent e) { - HexView table = (HexView) e.getSource(); - Point point = e.getPoint(); - int col = table.columnAtPoint(point); - int row = table.rowAtPoint(point); - int idx = getIdxByColAndRow(row, col); - mouseOverIdx = idx; - getModel().fireTableCellUpdated(row, col); - - if (listener != null) { - listener.byteMouseMoved(idx, idx == -1 ? 0 : getModel().getData()[idx]); - } - } - } - - public HexView() { - super(new HexViewTableModel(bytesInRow)); - highlightColors = new Color[highlightColorsStr.length]; - for (int i = 0; i < highlightColors.length; i++) { - highlightColors[i] = Color.decode("#" + highlightColorsStr[i]); - } - - setBackground(Color.white); - setFont(new Font("Monospaced", Font.PLAIN, 12)); - setTableHeader(new JTableHeader()); - setMaximumSize(new Dimension(200, 200)); - - setShowHorizontalLines(false); - setShowVerticalLines(false); - setRowSelectionAllowed(false); - setColumnSelectionAllowed(false); - - HighlightCellRenderer cellRenderer = new HighlightCellRenderer(); - TableColumn column = columnModel.getColumn(0); - column.setMaxWidth(80); - for (int i = 0; i < bytesInRow; i++) { - column = columnModel.getColumn(i + 1); - column.setMaxWidth(25); - column.setCellRenderer(cellRenderer); - } - - column = columnModel.getColumn(bytesInRow + 1); - column.setMaxWidth(10); - - for (int i = 0; i < bytesInRow; i++) { - column = columnModel.getColumn(i + bytesInRow + 1 + 1); - column.setMaxWidth(10); - column.setCellRenderer(cellRenderer); - } - - addMouseListener(new HexViewMouseAdapter()); - addMouseMotionListener(new HexViewMouseMotionAdapter()); - ListSelectionModel rowSelModel = getSelectionModel(); - ListSelectionModel colSelModel = getColumnModel().getSelectionModel(); - ListSelectionListener selectionListener = new HexViewSelectionListener(this); - rowSelModel.addListSelectionListener(selectionListener); - colSelModel.addListSelectionListener(selectionListener); - } - - @Override - public HexViewTableModel getModel() { - TableModel model = super.getModel(); - return (HexViewTableModel) model; - } - - public void setData(byte[] data, long[] highlightStarts, long[] highlightEnds) { - - if ((highlightStarts == null) ^ (highlightEnds == null)) { - throw new Error("highlightStarts and highlightEnds should be both null or not null."); - } - - if (highlightStarts != null && highlightStarts.length != highlightEnds.length) { - throw new Error("highlightStarts and highlightEnds should have the same number of elements."); - } - - getModel().setData(data); - this.highlightStarts = highlightStarts; - this.highlightEnds = highlightEnds; - } - - public byte[] getData() { - return getModel().getData(); - } - - public void selectByte(long byteNum) { - scrollToByte(byteNum); - listener.byteValueChanged((int) byteNum, getData()[(int) byteNum]); - } - - public void selectBytes(long byteNum, int length) { - selectionStart = (int) byteNum; - selectionEnd = (int) (byteNum + length - 1); - scrollToByte(new long[]{byteNum}, new long[]{byteNum + length - 1}); - listener.byteValueChanged((int) byteNum, getData()[(int) byteNum]); - getModel().fireTableDataChanged(); - } - - public void clearSelectedBytes() { - selectionStart = -1; - selectionEnd = -1; - getModel().fireTableDataChanged(); - } - - public void scrollToByte(long byteNum) { - - int row = (int) (byteNum / bytesInRow); - - //final int pageSize = (int) (getParent().getSize().getHeight() / getRowHeight()); - getSelectionModel().setSelectionInterval(row, row); - scrollRectToVisible(new Rectangle(getCellRect(row, 0, true))); - } - - private int getIdxByColAndRow(int row, int col) { - int idx = -1; - if (row < 0 || col < 0) { - return -1; - } - if (col > 0 && col != bytesInRow + 1) { - idx = row * bytesInRow + ((col > bytesInRow + 1) ? (col - bytesInRow - 2) : (col - 1)); - } - byte[] data = getModel().getData(); - if (idx >= data.length) { - idx = -1; - } - return idx; - } - - public int getFocusedByteIdx() { - int col = getSelectedColumn(); - int row = getSelectedRow(); - - int idx = getIdxByColAndRow(row, col); - return idx; - } - - public void scrollToByte(long[] byteNumStarts, long[] byteNumEnds) { - for (int i = 0; i < byteNumStarts.length; i++) { - scrollToByte(byteNumStarts[i]); - scrollToByte(byteNumEnds[i]); - scrollToByte(byteNumStarts[i]); - } - } - - public void addListener(HexViewListener listener) { - this.listener = listener; - } -} +/* + * Copyright (C) 2010-2016 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.hexview; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableColumn; +import javax.swing.table.TableModel; + +/** + * + * @author JPEXS + */ +public class HexView extends JTable { + + private static final int bytesInRow = 16; + + private long[] highlightStarts; + + private long[] highlightEnds; + + private final String[] highlightColorsStr = new String[]{/*"EEEEEE", */"29AEC2", "9AC88C", "DF5F80", "EEA32E", "FFD200", "5E9B4C", "D3E976", "A3AEC2"}; + + private final Color[] highlightColors; + + private final Color bgColor = Color.decode("#F7F7F7"); + + private final Color bgColorAlternate = Color.decode("#EDEDED"); + + private int mouseOverIdx = -1; + + private int selectionStart = -1; + + private int selectionEnd = -1; + + private HexViewListener listener; + + private class HighlightCellRenderer extends DefaultTableCellRenderer { + + public int byteIndex; + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) { + + JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col); + int level = -1; + int idx = getIdxByColAndRow(row, col); + if (highlightStarts != null) { + byteIndex = idx; + for (int i = 0; i < highlightStarts.length; i++) { + if (highlightStarts[i] <= idx && highlightEnds[i] >= idx) { + level++; + } else { + break; + } + } + } + + Color foreground; + Color background; + if (level > -1) { + foreground = Color.white; + background = highlightColors[level % highlightColors.length]; + } else { + foreground = Color.black; + background = row % 2 == 0 ? bgColor : bgColorAlternate; + } + + if (idx != -1 && (idx == mouseOverIdx + || (idx >= selectionStart && idx <= selectionEnd))) { + foreground = new Color(255 - foreground.getRed(), 255 - foreground.getGreen(), 255 - foreground.getBlue()); + background = new Color(255 - background.getRed(), 255 - background.getGreen(), 255 - background.getBlue()); + } + + l.setForeground(foreground); + l.setBackground(background); + + return l; + } + } + + private class HexViewSelectionListener implements ListSelectionListener { + + private final HexView table; + + public HexViewSelectionListener(HexView table) { + this.table = table; + } + + @Override + public void valueChanged(ListSelectionEvent e) { + + int col = table.getSelectedColumn(); + int row = table.getSelectedRow(); + + int idx = getIdxByColAndRow(row, col); + if (listener != null) { + listener.byteValueChanged(idx, idx == -1 ? 0 : getModel().getData()[idx]); + } + } + } + + private class HexViewMouseAdapter extends MouseAdapter { + + @Override + public void mouseExited(MouseEvent e) { + HexView table = (HexView) e.getSource(); + Point point = e.getPoint(); + int col = table.columnAtPoint(point); + int row = table.rowAtPoint(point); + mouseOverIdx = -1; + getModel().fireTableCellUpdated(row, col); + if (listener != null) { + listener.byteMouseMoved(-1, (byte) 0); + } + } + } + + private class HexViewMouseMotionAdapter extends MouseMotionAdapter { + + @Override + public void mouseMoved(MouseEvent e) { + HexView table = (HexView) e.getSource(); + Point point = e.getPoint(); + int col = table.columnAtPoint(point); + int row = table.rowAtPoint(point); + int idx = getIdxByColAndRow(row, col); + mouseOverIdx = idx; + getModel().fireTableCellUpdated(row, col); + + if (listener != null) { + listener.byteMouseMoved(idx, idx == -1 ? 0 : getModel().getData()[idx]); + } + } + } + + public HexView() { + super(new HexViewTableModel(bytesInRow)); + highlightColors = new Color[highlightColorsStr.length]; + for (int i = 0; i < highlightColors.length; i++) { + highlightColors[i] = Color.decode("#" + highlightColorsStr[i]); + } + + setBackground(Color.white); + setFont(new Font("Monospaced", Font.PLAIN, 12)); + setTableHeader(new JTableHeader()); + setMaximumSize(new Dimension(200, 200)); + + setShowHorizontalLines(false); + setShowVerticalLines(false); + setRowSelectionAllowed(false); + setColumnSelectionAllowed(false); + + HighlightCellRenderer cellRenderer = new HighlightCellRenderer(); + TableColumn column = columnModel.getColumn(0); + column.setMaxWidth(80); + for (int i = 0; i < bytesInRow; i++) { + column = columnModel.getColumn(i + 1); + column.setMaxWidth(25); + column.setCellRenderer(cellRenderer); + } + + column = columnModel.getColumn(bytesInRow + 1); + column.setMaxWidth(10); + + for (int i = 0; i < bytesInRow; i++) { + column = columnModel.getColumn(i + bytesInRow + 1 + 1); + column.setMaxWidth(10); + column.setCellRenderer(cellRenderer); + } + + addMouseListener(new HexViewMouseAdapter()); + addMouseMotionListener(new HexViewMouseMotionAdapter()); + ListSelectionModel rowSelModel = getSelectionModel(); + ListSelectionModel colSelModel = getColumnModel().getSelectionModel(); + ListSelectionListener selectionListener = new HexViewSelectionListener(this); + rowSelModel.addListSelectionListener(selectionListener); + colSelModel.addListSelectionListener(selectionListener); + } + + @Override + public HexViewTableModel getModel() { + TableModel model = super.getModel(); + return (HexViewTableModel) model; + } + + public void setData(byte[] data, long[] highlightStarts, long[] highlightEnds) { + + if ((highlightStarts == null) ^ (highlightEnds == null)) { + throw new Error("highlightStarts and highlightEnds should be both null or not null."); + } + + if (highlightStarts != null && highlightStarts.length != highlightEnds.length) { + throw new Error("highlightStarts and highlightEnds should have the same number of elements."); + } + + getModel().setData(data); + this.highlightStarts = highlightStarts; + this.highlightEnds = highlightEnds; + } + + public byte[] getData() { + return getModel().getData(); + } + + public void selectByte(long byteNum) { + scrollToByte(byteNum); + listener.byteValueChanged((int) byteNum, getData()[(int) byteNum]); + } + + public void selectBytes(long byteNum, int length) { + selectionStart = (int) byteNum; + selectionEnd = (int) (byteNum + length - 1); + scrollToByte(new long[]{byteNum}, new long[]{byteNum + length - 1}); + listener.byteValueChanged((int) byteNum, getData()[(int) byteNum]); + getModel().fireTableDataChanged(); + } + + public void clearSelectedBytes() { + selectionStart = -1; + selectionEnd = -1; + getModel().fireTableDataChanged(); + } + + public void scrollToByte(long byteNum) { + + int row = (int) (byteNum / bytesInRow); + + //final int pageSize = (int) (getParent().getSize().getHeight() / getRowHeight()); + getSelectionModel().setSelectionInterval(row, row); + scrollRectToVisible(new Rectangle(getCellRect(row, 0, true))); + } + + private int getIdxByColAndRow(int row, int col) { + int idx = -1; + if (row < 0 || col < 0) { + return -1; + } + if (col > 0 && col != bytesInRow + 1) { + idx = row * bytesInRow + ((col > bytesInRow + 1) ? (col - bytesInRow - 2) : (col - 1)); + } + byte[] data = getModel().getData(); + if (idx >= data.length) { + idx = -1; + } + return idx; + } + + public int getFocusedByteIdx() { + int col = getSelectedColumn(); + int row = getSelectedRow(); + + int idx = getIdxByColAndRow(row, col); + return idx; + } + + public void scrollToByte(long[] byteNumStarts, long[] byteNumEnds) { + for (int i = 0; i < byteNumStarts.length; i++) { + scrollToByte(byteNumStarts[i]); + scrollToByte(byteNumEnds[i]); + scrollToByte(byteNumStarts[i]); + } + } + + public void addListener(HexViewListener listener) { + this.listener = listener; + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index 16321b6a9..cea4b5a88 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -1,738 +1,738 @@ -# Copyright (C) 2010-2016 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 . - -menu.file = File -menu.file.open = Open... -menu.file.save = Save -menu.file.saveas = Save as... -menu.file.export.fla = Export to FLA -menu.file.export.all = Export all parts -menu.file.export.selection = Export selection -menu.file.exit = Exit - -menu.tools = Tools -menu.tools.searchas = Search All ActionScript... -menu.tools.proxy = Proxy -menu.tools.deobfuscation = Deobfuscation -menu.tools.deobfuscation.pcode = P-code deobfuscation... -menu.tools.deobfuscation.globalrename = Globally rename identifier -menu.tools.deobfuscation.renameinvalid = Rename invalid identifiers -menu.tools.gotoDocumentClass = Go to document class - -menu.settings = Settings -menu.settings.autodeobfuscation = Automatic deobfuscation -menu.settings.internalflashviewer = Use own Flash viewer -menu.settings.parallelspeedup = Parallel SpeedUp -menu.settings.disabledecompilation = Disable decompilation (Disassemble only) -menu.settings.addtocontextmenu = Add FFDec to SWF files context menu -menu.settings.language = Change language -menu.settings.cacheOnDisk = Use caching on disk -menu.settings.gotoMainClassOnStartup = Highlight document class on startup - -menu.help = Help -menu.help.checkupdates = Check for updates... -menu.help.helpus = Help us! -menu.help.homepage = Visit homepage -menu.help.about = About... - -contextmenu.remove = Remove - -button.save = Save -button.edit = Edit -button.cancel = Cancel -button.replace = Replace... - -notavailonthisplatform = Preview of this object is not available on this platform (Windows only). - -swfpreview = SWF preview -swfpreview.internal = SWF preview (Internal viewer) - -parameters = Parameters - -rename.enternew = Enter new name: - -rename.finished.identifier = Identifier renamed. -rename.finished.multiname = %count% multiname(s) renamed. - -node.texts = texts -node.images = images -node.movies = movies -node.sounds = sounds -node.binaryData = binaryData -node.fonts = fonts -node.sprites = sprites -node.shapes = shapes -node.morphshapes = morphshapes -node.buttons = buttons -node.frames = frames -node.scripts = scripts - -message.warning = Warning -message.confirm.experimental = Following procedure can damage SWF file which can be then unplayable.\r\nUSE IT ON YOUR OWN RISK. Do you want to continue? -message.confirm.parallel = Parallelism can speed up loading and decompilation but uses more memory. -message.confirm.on = Do you want to turn this ON? -message.confirm.off = Do you want to turn this OFF? -message.confirm = Confirm - -message.confirm.autodeobfuscate = Automatic deobfuscation is a way to decompile obfuscated code.\r\nDeobfuscation leads to slower decompilation and some of the dead code may be eliminated.\r\nIf the code is not obfuscated, it's better to turn autodeobfuscation off. - -message.parallel = Parallelism -message.trait.saved = Trait successfully saved - -message.constant.new.string = String "%value%" is not present in constants table. Do you want to add it? -message.constant.new.string.title = Add String -message.constant.new.integer = Integer value "%value%" is not present in constants table. Do you want to add it? -message.constant.new.integer.title = Add Integer -message.constant.new.unsignedinteger = Unsigned integer value "%value%" is not present in constants table. Do you want to add it? -message.constant.new.unsignedinteger.title = Add Unsigned integer -message.constant.new.double = Double value "%value%" is not present in constants table. Do you want to add it? -message.constant.new.double.title = Add Double - -work.buffering = Buffering -work.waitingfordissasembly = Waiting for disassembly -work.gettinghilights = Getting highlights -work.disassembling = Disassembling -work.exporting = Exporting -work.searching = Searching -work.renaming = Renaming -work.exporting.fla = Exporting FLA -work.renaming.identifiers = Renaming identifiers -work.deobfuscating = Deobfuscating -work.decompiling = Decompiling -work.gettingvariables = Getting variables -work.reading.swf = Reading SWF -work.creatingwindow = Creating window -work.buildingscripttree = Building script tree - -work.deobfuscating.complete = Deobfuscation complete - -message.search.notfound = String "%searchtext%" not found. -message.search.notfound.title = Not found - -message.rename.notfound.multiname = No multiname found under cursor -message.rename.notfound.identifier = No identifier found under cursor -message.rename.notfound.title = Not found -message.rename.renamed = Identifiers renamed: %count% - -filter.images = Images (%extensions%) -filter.fla = %version% Document (*.fla) -filter.xfl = %version% Uncompressed Document (*.xfl) -filter.swf = SWF files (*.swf) - -error = Error -error.image.invalid = Invalid image. - -error.text.invalid = Invalid text: %text% on line %line% -error.file.save = Cannot save file -error.file.write = Cannot write to the file -error.export = Error during export - -export.select.directory = Select directory to export -export.finishedin = Exported in %time% - -update.check.title = Update check -update.check.nonewversion = No new version available. - -message.helpus = Please visit\r\n%url%\r\nfor details. -message.homepage = Visit homepage at: \r\n%url% - -proxy = Proxy -proxy.start = Start proxy -proxy.stop = Stop proxy -proxy.show = Show proxy -exit = Exit - -panel.disassembled = P-code source -panel.decompiled = ActionScript source - -search.info = Search for "%text%": -search.script = Script - -constants = Constants -traits = Traits - -pleasewait = Please wait - -abc.detail.methodtrait = Method/Getter/Setter Trait -abc.detail.unsupported = - -abc.detail.slotconsttrait = Slot/Const Trait -abc.detail.traitname = Name: - -abc.detail.body.params.maxstack = Max stack: -abc.detail.body.params.localregcount = Local registers count: -abc.detail.body.params.minscope = Minimum scope depth: -abc.detail.body.params.maxscope = Maximum scope depth: -abc.detail.body.params.autofill = Auto fill on code save (GLOBAL SETTING) -abc.detail.body.params.autofill.experimental = ...EXPERIMENTAL - -abc.detail.methodinfo.methodindex = Method Index: -abc.detail.methodinfo.parameters = Parameters: -abc.detail.methodinfo.returnvalue = Return value type: - -error.methodinfo.params = MethodInfo Params Error -error.methodinfo.returnvalue = MethodInfo Return value type Error - -abc.detail.methodinfo = MethodInfo -abc.detail.body.code = MethodBody Code -abc.detail.body.params = MethodBody params - -abc.detail.slotconst.typevalue = Type and Value: - -error.slotconst.typevalue = SlotConst type value Error - -message.autofill.failed = Cannot get code stats for automatic body params.\r\nUncheck autofill to avoid this message. -info.selecttrait = Select class and click a trait in Actionscript source to edit it. - -button.viewgraph = View Graph -button.viewhex = View Hex - -abc.traitslist.instanceinitializer = instance initializer -abc.traitslist.classinitializer = class initializer - -action.edit.experimental = (Experimental) - -message.action.saved = Code successfully saved - -error.action.save = %error% on line %line% - -message.confirm.remove = Are you sure you want to remove %item%\n and all objects which depend on it? - -#after version 1.6.5u1: - -button.ok = OK -button.cancel = Cancel - -font.name = Font name: -font.isbold = Is bold: -font.isitalic = Is italic: -font.ascent = Ascent: -font.descent = Descent: -font.leading = Leading: -font.characters = Characters: -font.characters.add = Add characters: -value.unknown = ? - -yes = yes -no = no - -errors.present = There are ERRORS in the log. Click to view. -errors.none = There are no errors in the log. - -#after version 1.6.6: - -dialog.message.title = Message -dialog.select.title = Select an Option - -button.yes = Yes -button.no = No - -FileChooser.openButtonText = Open -FileChooser.openButtonToolTipText = Open -FileChooser.lookInLabelText = Look in: -FileChooser.acceptAllFileFilterText = All Files -FileChooser.filesOfTypeLabelText = Files of type: -FileChooser.fileNameLabelText = File name: -FileChooser.listViewButtonToolTipText = List -FileChooser.listViewButtonAccessibleName = List -FileChooser.detailsViewButtonToolTipText = Details -FileChooser.detailsViewButtonAccessibleName = Details -FileChooser.upFolderToolTipText = Up One Level -FileChooser.upFolderAccessibleName = Up One Level -FileChooser.homeFolderToolTipText = Home -FileChooser.homeFolderAccessibleName = Home -FileChooser.fileNameHeaderText = Name -FileChooser.fileSizeHeaderText = Size -FileChooser.fileTypeHeaderText = Type -FileChooser.fileDateHeaderText = Date -FileChooser.fileAttrHeaderText = Attributes -FileChooser.openDialogTitleText = Open -FileChooser.directoryDescriptionText = Directory -FileChooser.directoryOpenButtonText = Open -FileChooser.directoryOpenButtonToolTipText = Open selected directory -FileChooser.fileDescriptionText = Generic File -FileChooser.helpButtonText = Help -FileChooser.helpButtonToolTipText = FileChooser help -FileChooser.newFolderAccessibleName = New Folder -FileChooser.newFolderErrorText = Error creating new folder -FileChooser.newFolderToolTipText = Create New Folder -FileChooser.other.newFolder = NewFolder -FileChooser.other.newFolder.subsequent = NewFolder.{0} -FileChooser.win32.newFolder = New Folder -FileChooser.win32.newFolder.subsequent = New Folder ({0}) -FileChooser.saveButtonText = Save -FileChooser.saveButtonToolTipText = Save selected file -FileChooser.saveDialogTitleText = Save -FileChooser.saveInLabelText = Save in: -FileChooser.updateButtonText = Update -FileChooser.updateButtonToolTipText = Update directory listing - -#after version 1.6.6u2: - -FileChooser.detailsViewActionLabel.textAndMnemonic = Details -FileChooser.detailsViewButtonToolTip.textAndMnemonic = Details -FileChooser.fileAttrHeader.textAndMnemonic = Attributes -FileChooser.fileDateHeader.textAndMnemonic = Modified -FileChooser.fileNameHeader.textAndMnemonic = Name -FileChooser.fileNameLabel.textAndMnemonic = File name: -FileChooser.fileSizeHeader.textAndMnemonic = Size -FileChooser.fileTypeHeader.textAndMnemonic = Type -FileChooser.filesOfTypeLabel.textAndMnemonic = Files of type: -FileChooser.folderNameLabel.textAndMnemonic = Folder name: -FileChooser.homeFolderToolTip.textAndMnemonic = Home -FileChooser.listViewActionLabel.textAndMnemonic = List -FileChooser.listViewButtonToolTip.textAndMnemonic = List -FileChooser.lookInLabel.textAndMnemonic = Look in: -FileChooser.newFolderActionLabel.textAndMnemonic = New Folder -FileChooser.newFolderToolTip.textAndMnemonic = Create New Folder -FileChooser.refreshActionLabel.textAndMnemonic = Refresh -FileChooser.saveInLabel.textAndMnemonic = Save in: -FileChooser.upFolderToolTip.textAndMnemonic = Up One Level -FileChooser.viewMenuButtonAccessibleName = View Menu -FileChooser.viewMenuButtonToolTipText = View Menu -FileChooser.viewMenuLabel.textAndMnemonic = View -FileChooser.newFolderActionLabelText = New Folder -FileChooser.listViewActionLabelText = List -FileChooser.detailsViewActionLabelText = Details -FileChooser.refreshActionLabelText = Refresh -FileChooser.sortMenuLabelText = Arrange Icons By -FileChooser.viewMenuLabelText = View -FileChooser.fileSizeKiloBytes = {0} KB -FileChooser.fileSizeMegaBytes = {0} MB -FileChooser.fileSizeGigaBytes = {0} GB -FileChooser.folderNameLabelText = Folder name: - -error.occured = Error occurred: %error% -button.abort = Abort -button.retry = Retry -button.ignore = Ignore - -font.source = Source Font: - -#after version 1.6.7: - -menu.export = Export -menu.general = General -menu.language = Language - -startup.welcometo = Welcome to -startup.selectopen = Click Open icon on the top panel or drag SWF file to this window to start. - -error.font.nocharacter = Selected source font does not contain character "%char%". - -warning.initializers = Static fields and consts are often initialized in initializers.\nEditing value here is usually not enough! - -#after version 1.7.0u1: - -menu.tools.searchMemory = Search SWFs in memory -menu.file.reload = Reload -message.confirm.reload = This action cancels all unsaved changes and reloads the SWF file again.\nDo you want to continue? - -dialog.selectbkcolor.title = Select background color for SWF display -button.selectbkcolor.hint = Select background color - -ColorChooser.okText = OK -ColorChooser.cancelText = Cancel -ColorChooser.resetText = Reset -ColorChooser.previewText = Preview -ColorChooser.swatchesNameText = Swatches -ColorChooser.swatchesRecentText = Recent: -ColorChooser.sampleText = Sample Text Sample Text - -#after version 1.7.1: - -preview.play = Play -preview.pause = Pause -preview.stop = Stop - -message.confirm.removemultiple = Are you sure you want to remove %count% items\n and all objects which depend on it? - -menu.tools.searchCache = Search browsers cache - -#after version 1.7.2u2 - -error.trait.exists = Trait with name "%name%" already exists. -button.addtrait = Add trait -button.font.embed = Embed... -button.yes.all = Yes to all -button.no.all = No to all -message.font.add.exists = Character %char% already exists in the font tag.\nDo you want to replace it? - -filter.gfx = ScaleForm GFx files (*.gfx) -filter.supported = All supported filetypes -work.canceled = Canceled -work.restoringControlFlow = Restoring control flow -menu.advancedsettings.advancedsettings = Advanced Settings -menu.recentFiles = Recent files - -#after version 1.7.4 -work.restoringControlFlow.complete = Control flow restored -message.confirm.recentFileNotFound = File not found. Do you want to remove it from the recent file list? -contextmenu.closeSwf = Close SWF -menu.settings.autoRenameIdentifiers = Auto rename identifiers -menu.file.saveasexe = Save as Exe... -filter.exe = Executable files (*.exe) - -#after version 1.8.0 -font.updateTexts = Update texts - -#after version 1.8.0u1 -menu.file.close = Close -menu.file.closeAll = Close all -menu.tools.otherTools = Other -menu.tools.otherTools.clearRecentFiles = Clear recent files -fontName.name = Font display name: -fontName.copyright = Font copyright: -button.preview = Preview -button.reset = Reset -errors.info = There are INFORMATIONS in the log. Click to view. -errors.warning = There are WARNINGS in the log. Click to view. - -decompilationError = Decompilation error - -disassemblingProgress.toString = toString -disassemblingProgress.reading = Reading -disassemblingProgress.deobfuscating = Deobfuscating - -contextmenu.moveTag = Move tag to - -filter.swc = SWC component files (*.swc) -filter.zip = ZIP compressed files (*.zip) -filter.binary = Binary search - all files (*.*) - -open.error = Error -open.error.fileNotFound = File not found -open.error.cannotOpen = Cannot open file - -node.others = others - -#after version 1.8.1 -menu.tools.search = Text Search - -#after version 1.8.1u1 -menu.tools.timeline = Timeline - -dialog.selectcolor.title = Select color -button.selectcolor.hint = Click to select color - -#default item name, will be used in following sentences -generictag.array.item = item -generictag.array.insertbeginning = Insert %item% at the beginning -generictag.array.insertbefore = Insert %item% before -generictag.array.remove = Remove %item% -generictag.array.insertafter = Insert %item% after -generictag.array.insertend = Insert %item% at the end - -#after version 2.0.0 -contextmenu.expandAll = Expand all - -filter.sounds = Supported sound formats (*.wav, *.mp3) -filter.sounds.wav = Wave file format (*.wav) -filter.sounds.mp3 = MP3 compressed format (*.mp3) - -error.sound.invalid = Invalid sound. - -button.prev = Previous -button.next = Next - -#after version 2.1.0 -message.action.playerglobal.title = PlayerGlobal library needed -message.action.playerglobal.needed = For ActionScript 3 direct editation, a library called "PlayerGlobal.swc" needs to be downloaded from Adobe homepage.\r\n%adobehomepage%\r\nPress OK to go to the download page. -message.action.playerglobal.place = Download the library called PlayerGlobal(.swc), and place it to directory\r\n%libpath%\r\n Press OK to continue. - -message.confirm.experimental.function = This function is EXPERIMENTAL. It means that you should not trust the results and the SWF file can be disfunctional after saving. -message.confirm.donotshowagain = Do not show again - -menu.import = Import -menu.file.import.text = Import text -import.select.directory = Select directory to import -error.text.import = Error during text import. Do you want to continue? - -#after version 2.1.1 -contextmenu.removeWithDependencies = Remove with dependencies - -abc.action.find-usages = Find usages -abc.action.find-declaration = Find declaration - -contextmenu.rawEdit = Raw edit -contextmenu.jumpToCharacter = Jump to character - -menu.settings.dumpView = Dump view - -menu.view = View -menu.file.view.resources = Resources -menu.file.view.hex = Hex dump - -node.header = header - -header.signature = Signature: -header.compression = Compression: -header.compression.lzma = LZMA -header.compression.zlib = ZLIB -header.compression.none = No compression -header.version = SWF Version: -header.gfx = GFX: -header.filesize = File size: -header.framerate = Frame rate: -header.framecount = Frame count: -header.displayrect = Display rect: -header.displayrect.value.twips = %xmin%,%ymin% => %xmax%,%ymax% twips -header.displayrect.value.pixels = %xmin%,%ymin% => %xmax%,%ymax% pixels - -#after version 2.1.2 -contextmenu.saveToFile = Save to File -contextmenu.parseActions = Parse actions -contextmenu.parseABC = Parse ABC -contextmenu.parseInstructions = Parse AVM2 Instructions - -#after version 2.1.3 -menu.deobfuscation = Deobfuscation -menu.file.deobfuscation.old = Old style -menu.file.deobfuscation.new = New style - -#after version 2.1.4 -contextmenu.openswfinside = Open SWF inside -binarydata.swfInside = It looks like there is SWF inside this binary data tag. Click here to load it as subtree. - -#after version 3.0.0 -button.zoomin.hint = Zoom in -button.zoomout.hint = Zoom out -button.zoomfit.hint = Zoom to fit -button.zoomnone.hint = Zoom to 1:1 -button.snapshot.hint = Take snapshot into clipboard - -editorTruncateWarning = Text truncated at position %chars% in debug mode. - -#Font name which is presented in the SWF Font tag -font.name.intag = Font name in tag: - -menu.debugger = Debugger -menu.debugger.switch = Debugger -menu.debugger.replacetrace = Replace trace calls -menu.debugger.showlog = Show Log - -message.debugger = This SWF Debugger can only be used to print messages to log window, browser console or alerts.\r\nIt is NOT designed for features like step code, breakpoints etc. - -contextmenu.addTag = Add tag - -deobfuscation.comment.tryenable = Tip: You can try enabling "Automatic deobfuscation" in Settings -deobfuscation.comment.failed = Deobfuscation is activated but decompilation still failed. If the file is NOT obfuscated, disable "Automatic deobfuscation" for better results. - -#after version 4.0.2 -preview.nextframe = Next frame -preview.prevframe = Previous frame -preview.gotoframe = Goto frame... - -preview.gotoframe.dialog.title = Goto frame -preview.gotoframe.dialog.message = Enter frame number (%min% - %max%) -preview.gotoframe.dialog.frame.error = Invalid frame number. It must be number between %min% and %max%. - -error.text.invalid.continue = Invalid text: %text% on line %line%. Do you want to continue? - -#after version 4.0.5 -contextmenu.copyTag = Copy tag to -fit = fit -button.setAdvanceValues = Set advance values - -menu.tools.replace = Text Replace - -message.confirm.close = There are unsaved changes. Do you really want to close {swfName}? -message.confirm.closeAll = There are unsaved changes. Do you really want to close all SWFs? - -contextmenu.exportJavaSource = Export Java Source -contextmenu.exportSwfXml = Export SWF as XML -contextmenu.importSwfXml = Import SWF XML - -filter.xml = XML - -#after version 4.1.0 -contextmenu.undo = Undo - -text.align.left = Left align -text.align.right = Right align -text.align.center = Center align -text.align.justify = Justify align - -text.undo = Undo changes - -menu.file.import.xml = Import SWF XML -menu.file.export.xml = Export SWF XML - -#after version 4.1.1 -text.align.translatex.decrease = Decrease TranslateX -text.align.translatex.increase = Increase TranslateX -selectPreviousTag = Select previous tag -selectNextTag = Select next tag -button.ignoreAll = Ignore All -menu.file.import.symbolClass = Import Symbol-Class -text.toggleCase = Toggle case - -#after version 5.0.2 -preview.loop = Loop -menu.file.import.script = Import script -contextmenu.copyTagWithDependencies = Copy tag with dependencies to -button.replaceWithTag = Replace with other character tag -button.resolveConstants = Resolve constants - -#after version 5.1.0 -button.viewConstants = View Constants -work.exported = Exported -button.replaceAlphaChannel = Replace alpha channel... - -tagInfo.header.name = Name -tagInfo.header.value = Value -tagInfo.tagType = Tag Type -tagInfo.characterId = Character Id -tagInfo.offset = Offset -tagInfo.length = Length -tagInfo.bounds = Bounds -tagInfo.width = Width -tagInfo.height = Height -tagInfo.neededCharacters = Needed Characters - -button.viewhexpcode = View Hex with instructions -taginfo.header = Basic tag info - -tagInfo.dependentCharacters = Dependent Characters - -#after version 5.3.0 -header.uncompressed = Uncompressed -header.warning.unsupportedGfxCompression = GFX supports only uncompressed or Zlib compressed content. -header.warning.minimumZlibVersion = Zlib compression needs SWF version 6 or greater. -header.warning.minimumLzmaVersion = LZMA compression needs SWF version 13 or greater. - -tagInfo.codecName = Codec Name -tagInfo.exportFormat = Export Format -tagInfo.samplingRate = Sampling Rate -tagInfo.stereo = Stereo -tagInfo.sampleCount = Sample Count - -filter.dmg = Mac Executable files (*.dmg) -filter.linuxExe = Linux Executable files - -import.script.result = %count% scripts imported. -import.script.as12warning = Import script can import only AS1/2 scripts. - -error.constantPoolTooBig = Constant pool is too big. index=%index%, size=%size% -error.image.alpha.invalid = Invalid alpha channel data. - -#after version 6.0.2 -contextmenu.saveUncompressedToFile = Save to Uncompressed File -abc.traitslist.scriptinitializer = script initializer -menu.settings.autoOpenLoadedSWFs = Open loaded SWFs while playing - -#after version 6.1.1 -menu.file.start = Start -menu.file.start.run = Run -menu.file.start.stop = Stop -menu.file.start.debug = Debug -menu.debugging = Debugging -menu.debugging.debug = Debug -menu.debugging.debug.stop = Stop -menu.debugging.debug.pause = Pause -menu.debugging.debug.stepOver = Step over -menu.debugging.debug.stepInto = Step into -menu.debugging.debug.stepOut = Step out -menu.debugging.debug.continue = Continue -menu.debugging.debug.stack = Stack... -menu.debugging.debug.watch = New watch... - -message.playerpath.notset = Flash Player projector not found. Please configure its path in Advanced Settings / Paths (1). -message.playerpath.debug.notset = Flash Player projector content debugger not found. Please configure its path in Advanced Settings / Paths (2). -message.playerpath.lib.notset = PlayerGlobal (.SWC) not found. Please configure its path in Advanced Settings / Paths (3). - -debugpanel.header = Debugging - -variables.header.registers = Registers -variables.header.locals = Locals -variables.header.arguments = Arguments -variables.header.scopeChain = Scope chain -variables.column.name = Name -variables.column.type = Type -variables.column.value = Value - -callStack.header = Call stack -callStack.header.file = File -callStack.header.line = Line - -stack.header = Stack -stack.header.item = Item - -constantpool.header = Constant pool -constantpool.header.id = Id -constantpool.header.value = Value - -work.running = Running -work.debugging = Debugging -work.debugging.instrumenting = Preparing SWF for debugging -work.breakat = Break at\u0020 -work.halted = Debugging started, execution halted. Add breakpoints and click Continue (F5) to resume running. - -debuglog.header = Log -debuglog.button.clear = Clear - -#after 7.0.1 -work.debugging.wait = Waiting for Flash debug projector to connect - -error.debug.listen = Cannot listen on port %port%. There might be other flash debugger running. - -debug.break.reason.unknown = (Unknown) -debug.break.reason.breakpoint = (Breakpoint) -debug.break.reason.watch = (Watch) -debug.break.reason.fault = (Fault) -debug.break.reason.stopRequest = (Stop request) -debug.break.reason.step = (Step) -debug.break.reason.halt = (Halt) -debug.break.reason.scriptLoaded = (Script loaded) - -menu.file.start.debugpcode = Debug P-code - -#after 7.1.2 -button.replaceNoFill = Replace - Update bounds... -message.warning.svgImportExperimental = Not all SVG features are supported. Please check the log after import. - -message.imported.swf = The SWF file uses assets from an imported SWF file:\n%url%\nDo you want the assets to be loaded from that URL? -message.imported.swf.manually = Cannot load imported SWF\n%url%\nThe file or URL does not exist.\nDo you want to select local file? - -message.warning.hexViewNotUpToDate = Hex View is not up-to-date. Please save and reload the file to update Hex View. -message.font.replace.updateTexts = Some characters were replaced. Do you want to update the existing texts? - -menu.settings.simplifyExpressions = Simplify expressions - -#after 8.0.1 -menu.recentFiles.empty = Recent file list is empty -message.warning.outOfMemory32BitJre = OutOfMemory error occurred. You are running 32bit Java on 64bit system. Please use 64bit Java. - -menu.file.reloadAll = Reload all -message.confirm.reloadAll = This action cancels all unsaved changes in all SWF files and reloads whole application again.\nDo you want to continue? -export.script.singleFilePallelModeWarning = Single file script export is not supported with enabled parallel speedup - -button.showOriginalBytesInPcodeHex = Show original bytes -button.remove = Remove -button.showFileOffsetInPcodeHex = Show file offset - -generic.editor.amf3.title = AMF3 editor -generic.editor.amf3.help = AMF3 value syntax:\n\ - ------------------\n\ - scalar types:\n\ - %scalar_samples%\ - other types:\n\ - %nonscalar_samples%\ - \n\ - Notes:\n\ - \ * Nonscalar datatypes can be referenced by previously declared "id" attributes with # syntax:\n\ - %reference_sample%\n\ - \ * Keys in Dictionary entries can be any type\n -contextmenu.showInResources = Show in Resources +# Copyright (C) 2010-2016 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 . + +menu.file = File +menu.file.open = Open... +menu.file.save = Save +menu.file.saveas = Save as... +menu.file.export.fla = Export to FLA +menu.file.export.all = Export all parts +menu.file.export.selection = Export selection +menu.file.exit = Exit + +menu.tools = Tools +menu.tools.searchas = Search All ActionScript... +menu.tools.proxy = Proxy +menu.tools.deobfuscation = Deobfuscation +menu.tools.deobfuscation.pcode = P-code deobfuscation... +menu.tools.deobfuscation.globalrename = Globally rename identifier +menu.tools.deobfuscation.renameinvalid = Rename invalid identifiers +menu.tools.gotoDocumentClass = Go to document class + +menu.settings = Settings +menu.settings.autodeobfuscation = Automatic deobfuscation +menu.settings.internalflashviewer = Use own Flash viewer +menu.settings.parallelspeedup = Parallel SpeedUp +menu.settings.disabledecompilation = Disable decompilation (Disassemble only) +menu.settings.addtocontextmenu = Add FFDec to SWF files context menu +menu.settings.language = Change language +menu.settings.cacheOnDisk = Use caching on disk +menu.settings.gotoMainClassOnStartup = Highlight document class on startup + +menu.help = Help +menu.help.checkupdates = Check for updates... +menu.help.helpus = Help us! +menu.help.homepage = Visit homepage +menu.help.about = About... + +contextmenu.remove = Remove + +button.save = Save +button.edit = Edit +button.cancel = Cancel +button.replace = Replace... + +notavailonthisplatform = Preview of this object is not available on this platform (Windows only). + +swfpreview = SWF preview +swfpreview.internal = SWF preview (Internal viewer) + +parameters = Parameters + +rename.enternew = Enter new name: + +rename.finished.identifier = Identifier renamed. +rename.finished.multiname = %count% multiname(s) renamed. + +node.texts = texts +node.images = images +node.movies = movies +node.sounds = sounds +node.binaryData = binaryData +node.fonts = fonts +node.sprites = sprites +node.shapes = shapes +node.morphshapes = morphshapes +node.buttons = buttons +node.frames = frames +node.scripts = scripts + +message.warning = Warning +message.confirm.experimental = Following procedure can damage SWF file which can be then unplayable.\r\nUSE IT ON YOUR OWN RISK. Do you want to continue? +message.confirm.parallel = Parallelism can speed up loading and decompilation but uses more memory. +message.confirm.on = Do you want to turn this ON? +message.confirm.off = Do you want to turn this OFF? +message.confirm = Confirm + +message.confirm.autodeobfuscate = Automatic deobfuscation is a way to decompile obfuscated code.\r\nDeobfuscation leads to slower decompilation and some of the dead code may be eliminated.\r\nIf the code is not obfuscated, it's better to turn autodeobfuscation off. + +message.parallel = Parallelism +message.trait.saved = Trait successfully saved + +message.constant.new.string = String "%value%" is not present in constants table. Do you want to add it? +message.constant.new.string.title = Add String +message.constant.new.integer = Integer value "%value%" is not present in constants table. Do you want to add it? +message.constant.new.integer.title = Add Integer +message.constant.new.unsignedinteger = Unsigned integer value "%value%" is not present in constants table. Do you want to add it? +message.constant.new.unsignedinteger.title = Add Unsigned integer +message.constant.new.double = Double value "%value%" is not present in constants table. Do you want to add it? +message.constant.new.double.title = Add Double + +work.buffering = Buffering +work.waitingfordissasembly = Waiting for disassembly +work.gettinghilights = Getting highlights +work.disassembling = Disassembling +work.exporting = Exporting +work.searching = Searching +work.renaming = Renaming +work.exporting.fla = Exporting FLA +work.renaming.identifiers = Renaming identifiers +work.deobfuscating = Deobfuscating +work.decompiling = Decompiling +work.gettingvariables = Getting variables +work.reading.swf = Reading SWF +work.creatingwindow = Creating window +work.buildingscripttree = Building script tree + +work.deobfuscating.complete = Deobfuscation complete + +message.search.notfound = String "%searchtext%" not found. +message.search.notfound.title = Not found + +message.rename.notfound.multiname = No multiname found under cursor +message.rename.notfound.identifier = No identifier found under cursor +message.rename.notfound.title = Not found +message.rename.renamed = Identifiers renamed: %count% + +filter.images = Images (%extensions%) +filter.fla = %version% Document (*.fla) +filter.xfl = %version% Uncompressed Document (*.xfl) +filter.swf = SWF files (*.swf) + +error = Error +error.image.invalid = Invalid image. + +error.text.invalid = Invalid text: %text% on line %line% +error.file.save = Cannot save file +error.file.write = Cannot write to the file +error.export = Error during export + +export.select.directory = Select directory to export +export.finishedin = Exported in %time% + +update.check.title = Update check +update.check.nonewversion = No new version available. + +message.helpus = Please visit\r\n%url%\r\nfor details. +message.homepage = Visit homepage at: \r\n%url% + +proxy = Proxy +proxy.start = Start proxy +proxy.stop = Stop proxy +proxy.show = Show proxy +exit = Exit + +panel.disassembled = P-code source +panel.decompiled = ActionScript source + +search.info = Search for "%text%": +search.script = Script + +constants = Constants +traits = Traits + +pleasewait = Please wait + +abc.detail.methodtrait = Method/Getter/Setter Trait +abc.detail.unsupported = - +abc.detail.slotconsttrait = Slot/Const Trait +abc.detail.traitname = Name: + +abc.detail.body.params.maxstack = Max stack: +abc.detail.body.params.localregcount = Local registers count: +abc.detail.body.params.minscope = Minimum scope depth: +abc.detail.body.params.maxscope = Maximum scope depth: +abc.detail.body.params.autofill = Auto fill on code save (GLOBAL SETTING) +abc.detail.body.params.autofill.experimental = ...EXPERIMENTAL + +abc.detail.methodinfo.methodindex = Method Index: +abc.detail.methodinfo.parameters = Parameters: +abc.detail.methodinfo.returnvalue = Return value type: + +error.methodinfo.params = MethodInfo Params Error +error.methodinfo.returnvalue = MethodInfo Return value type Error + +abc.detail.methodinfo = MethodInfo +abc.detail.body.code = MethodBody Code +abc.detail.body.params = MethodBody params + +abc.detail.slotconst.typevalue = Type and Value: + +error.slotconst.typevalue = SlotConst type value Error + +message.autofill.failed = Cannot get code stats for automatic body params.\r\nUncheck autofill to avoid this message. +info.selecttrait = Select class and click a trait in Actionscript source to edit it. + +button.viewgraph = View Graph +button.viewhex = View Hex + +abc.traitslist.instanceinitializer = instance initializer +abc.traitslist.classinitializer = class initializer + +action.edit.experimental = (Experimental) + +message.action.saved = Code successfully saved + +error.action.save = %error% on line %line% + +message.confirm.remove = Are you sure you want to remove %item%\n and all objects which depend on it? + +#after version 1.6.5u1: + +button.ok = OK +button.cancel = Cancel + +font.name = Font name: +font.isbold = Is bold: +font.isitalic = Is italic: +font.ascent = Ascent: +font.descent = Descent: +font.leading = Leading: +font.characters = Characters: +font.characters.add = Add characters: +value.unknown = ? + +yes = yes +no = no + +errors.present = There are ERRORS in the log. Click to view. +errors.none = There are no errors in the log. + +#after version 1.6.6: + +dialog.message.title = Message +dialog.select.title = Select an Option + +button.yes = Yes +button.no = No + +FileChooser.openButtonText = Open +FileChooser.openButtonToolTipText = Open +FileChooser.lookInLabelText = Look in: +FileChooser.acceptAllFileFilterText = All Files +FileChooser.filesOfTypeLabelText = Files of type: +FileChooser.fileNameLabelText = File name: +FileChooser.listViewButtonToolTipText = List +FileChooser.listViewButtonAccessibleName = List +FileChooser.detailsViewButtonToolTipText = Details +FileChooser.detailsViewButtonAccessibleName = Details +FileChooser.upFolderToolTipText = Up One Level +FileChooser.upFolderAccessibleName = Up One Level +FileChooser.homeFolderToolTipText = Home +FileChooser.homeFolderAccessibleName = Home +FileChooser.fileNameHeaderText = Name +FileChooser.fileSizeHeaderText = Size +FileChooser.fileTypeHeaderText = Type +FileChooser.fileDateHeaderText = Date +FileChooser.fileAttrHeaderText = Attributes +FileChooser.openDialogTitleText = Open +FileChooser.directoryDescriptionText = Directory +FileChooser.directoryOpenButtonText = Open +FileChooser.directoryOpenButtonToolTipText = Open selected directory +FileChooser.fileDescriptionText = Generic File +FileChooser.helpButtonText = Help +FileChooser.helpButtonToolTipText = FileChooser help +FileChooser.newFolderAccessibleName = New Folder +FileChooser.newFolderErrorText = Error creating new folder +FileChooser.newFolderToolTipText = Create New Folder +FileChooser.other.newFolder = NewFolder +FileChooser.other.newFolder.subsequent = NewFolder.{0} +FileChooser.win32.newFolder = New Folder +FileChooser.win32.newFolder.subsequent = New Folder ({0}) +FileChooser.saveButtonText = Save +FileChooser.saveButtonToolTipText = Save selected file +FileChooser.saveDialogTitleText = Save +FileChooser.saveInLabelText = Save in: +FileChooser.updateButtonText = Update +FileChooser.updateButtonToolTipText = Update directory listing + +#after version 1.6.6u2: + +FileChooser.detailsViewActionLabel.textAndMnemonic = Details +FileChooser.detailsViewButtonToolTip.textAndMnemonic = Details +FileChooser.fileAttrHeader.textAndMnemonic = Attributes +FileChooser.fileDateHeader.textAndMnemonic = Modified +FileChooser.fileNameHeader.textAndMnemonic = Name +FileChooser.fileNameLabel.textAndMnemonic = File name: +FileChooser.fileSizeHeader.textAndMnemonic = Size +FileChooser.fileTypeHeader.textAndMnemonic = Type +FileChooser.filesOfTypeLabel.textAndMnemonic = Files of type: +FileChooser.folderNameLabel.textAndMnemonic = Folder name: +FileChooser.homeFolderToolTip.textAndMnemonic = Home +FileChooser.listViewActionLabel.textAndMnemonic = List +FileChooser.listViewButtonToolTip.textAndMnemonic = List +FileChooser.lookInLabel.textAndMnemonic = Look in: +FileChooser.newFolderActionLabel.textAndMnemonic = New Folder +FileChooser.newFolderToolTip.textAndMnemonic = Create New Folder +FileChooser.refreshActionLabel.textAndMnemonic = Refresh +FileChooser.saveInLabel.textAndMnemonic = Save in: +FileChooser.upFolderToolTip.textAndMnemonic = Up One Level +FileChooser.viewMenuButtonAccessibleName = View Menu +FileChooser.viewMenuButtonToolTipText = View Menu +FileChooser.viewMenuLabel.textAndMnemonic = View +FileChooser.newFolderActionLabelText = New Folder +FileChooser.listViewActionLabelText = List +FileChooser.detailsViewActionLabelText = Details +FileChooser.refreshActionLabelText = Refresh +FileChooser.sortMenuLabelText = Arrange Icons By +FileChooser.viewMenuLabelText = View +FileChooser.fileSizeKiloBytes = {0} KB +FileChooser.fileSizeMegaBytes = {0} MB +FileChooser.fileSizeGigaBytes = {0} GB +FileChooser.folderNameLabelText = Folder name: + +error.occured = Error occurred: %error% +button.abort = Abort +button.retry = Retry +button.ignore = Ignore + +font.source = Source Font: + +#after version 1.6.7: + +menu.export = Export +menu.general = General +menu.language = Language + +startup.welcometo = Welcome to +startup.selectopen = Click Open icon on the top panel or drag SWF file to this window to start. + +error.font.nocharacter = Selected source font does not contain character "%char%". + +warning.initializers = Static fields and consts are often initialized in initializers.\nEditing value here is usually not enough! + +#after version 1.7.0u1: + +menu.tools.searchMemory = Search SWFs in memory +menu.file.reload = Reload +message.confirm.reload = This action cancels all unsaved changes and reloads the SWF file again.\nDo you want to continue? + +dialog.selectbkcolor.title = Select background color for SWF display +button.selectbkcolor.hint = Select background color + +ColorChooser.okText = OK +ColorChooser.cancelText = Cancel +ColorChooser.resetText = Reset +ColorChooser.previewText = Preview +ColorChooser.swatchesNameText = Swatches +ColorChooser.swatchesRecentText = Recent: +ColorChooser.sampleText = Sample Text Sample Text + +#after version 1.7.1: + +preview.play = Play +preview.pause = Pause +preview.stop = Stop + +message.confirm.removemultiple = Are you sure you want to remove %count% items\n and all objects which depend on it? + +menu.tools.searchCache = Search browsers cache + +#after version 1.7.2u2 + +error.trait.exists = Trait with name "%name%" already exists. +button.addtrait = Add trait +button.font.embed = Embed... +button.yes.all = Yes to all +button.no.all = No to all +message.font.add.exists = Character %char% already exists in the font tag.\nDo you want to replace it? + +filter.gfx = ScaleForm GFx files (*.gfx) +filter.supported = All supported filetypes +work.canceled = Canceled +work.restoringControlFlow = Restoring control flow +menu.advancedsettings.advancedsettings = Advanced Settings +menu.recentFiles = Recent files + +#after version 1.7.4 +work.restoringControlFlow.complete = Control flow restored +message.confirm.recentFileNotFound = File not found. Do you want to remove it from the recent file list? +contextmenu.closeSwf = Close SWF +menu.settings.autoRenameIdentifiers = Auto rename identifiers +menu.file.saveasexe = Save as Exe... +filter.exe = Executable files (*.exe) + +#after version 1.8.0 +font.updateTexts = Update texts + +#after version 1.8.0u1 +menu.file.close = Close +menu.file.closeAll = Close all +menu.tools.otherTools = Other +menu.tools.otherTools.clearRecentFiles = Clear recent files +fontName.name = Font display name: +fontName.copyright = Font copyright: +button.preview = Preview +button.reset = Reset +errors.info = There are INFORMATIONS in the log. Click to view. +errors.warning = There are WARNINGS in the log. Click to view. + +decompilationError = Decompilation error + +disassemblingProgress.toString = toString +disassemblingProgress.reading = Reading +disassemblingProgress.deobfuscating = Deobfuscating + +contextmenu.moveTag = Move tag to + +filter.swc = SWC component files (*.swc) +filter.zip = ZIP compressed files (*.zip) +filter.binary = Binary search - all files (*.*) + +open.error = Error +open.error.fileNotFound = File not found +open.error.cannotOpen = Cannot open file + +node.others = others + +#after version 1.8.1 +menu.tools.search = Text Search + +#after version 1.8.1u1 +menu.tools.timeline = Timeline + +dialog.selectcolor.title = Select color +button.selectcolor.hint = Click to select color + +#default item name, will be used in following sentences +generictag.array.item = item +generictag.array.insertbeginning = Insert %item% at the beginning +generictag.array.insertbefore = Insert %item% before +generictag.array.remove = Remove %item% +generictag.array.insertafter = Insert %item% after +generictag.array.insertend = Insert %item% at the end + +#after version 2.0.0 +contextmenu.expandAll = Expand all + +filter.sounds = Supported sound formats (*.wav, *.mp3) +filter.sounds.wav = Wave file format (*.wav) +filter.sounds.mp3 = MP3 compressed format (*.mp3) + +error.sound.invalid = Invalid sound. + +button.prev = Previous +button.next = Next + +#after version 2.1.0 +message.action.playerglobal.title = PlayerGlobal library needed +message.action.playerglobal.needed = For ActionScript 3 direct editation, a library called "PlayerGlobal.swc" needs to be downloaded from Adobe homepage.\r\n%adobehomepage%\r\nPress OK to go to the download page. +message.action.playerglobal.place = Download the library called PlayerGlobal(.swc), and place it to directory\r\n%libpath%\r\n Press OK to continue. + +message.confirm.experimental.function = This function is EXPERIMENTAL. It means that you should not trust the results and the SWF file can be disfunctional after saving. +message.confirm.donotshowagain = Do not show again + +menu.import = Import +menu.file.import.text = Import text +import.select.directory = Select directory to import +error.text.import = Error during text import. Do you want to continue? + +#after version 2.1.1 +contextmenu.removeWithDependencies = Remove with dependencies + +abc.action.find-usages = Find usages +abc.action.find-declaration = Find declaration + +contextmenu.rawEdit = Raw edit +contextmenu.jumpToCharacter = Jump to character + +menu.settings.dumpView = Dump view + +menu.view = View +menu.file.view.resources = Resources +menu.file.view.hex = Hex dump + +node.header = header + +header.signature = Signature: +header.compression = Compression: +header.compression.lzma = LZMA +header.compression.zlib = ZLIB +header.compression.none = No compression +header.version = SWF Version: +header.gfx = GFX: +header.filesize = File size: +header.framerate = Frame rate: +header.framecount = Frame count: +header.displayrect = Display rect: +header.displayrect.value.twips = %xmin%,%ymin% => %xmax%,%ymax% twips +header.displayrect.value.pixels = %xmin%,%ymin% => %xmax%,%ymax% pixels + +#after version 2.1.2 +contextmenu.saveToFile = Save to File +contextmenu.parseActions = Parse actions +contextmenu.parseABC = Parse ABC +contextmenu.parseInstructions = Parse AVM2 Instructions + +#after version 2.1.3 +menu.deobfuscation = Deobfuscation +menu.file.deobfuscation.old = Old style +menu.file.deobfuscation.new = New style + +#after version 2.1.4 +contextmenu.openswfinside = Open SWF inside +binarydata.swfInside = It looks like there is SWF inside this binary data tag. Click here to load it as subtree. + +#after version 3.0.0 +button.zoomin.hint = Zoom in +button.zoomout.hint = Zoom out +button.zoomfit.hint = Zoom to fit +button.zoomnone.hint = Zoom to 1:1 +button.snapshot.hint = Take snapshot into clipboard + +editorTruncateWarning = Text truncated at position %chars% in debug mode. + +#Font name which is presented in the SWF Font tag +font.name.intag = Font name in tag: + +menu.debugger = Debugger +menu.debugger.switch = Debugger +menu.debugger.replacetrace = Replace trace calls +menu.debugger.showlog = Show Log + +message.debugger = This SWF Debugger can only be used to print messages to log window, browser console or alerts.\r\nIt is NOT designed for features like step code, breakpoints etc. + +contextmenu.addTag = Add tag + +deobfuscation.comment.tryenable = Tip: You can try enabling "Automatic deobfuscation" in Settings +deobfuscation.comment.failed = Deobfuscation is activated but decompilation still failed. If the file is NOT obfuscated, disable "Automatic deobfuscation" for better results. + +#after version 4.0.2 +preview.nextframe = Next frame +preview.prevframe = Previous frame +preview.gotoframe = Goto frame... + +preview.gotoframe.dialog.title = Goto frame +preview.gotoframe.dialog.message = Enter frame number (%min% - %max%) +preview.gotoframe.dialog.frame.error = Invalid frame number. It must be number between %min% and %max%. + +error.text.invalid.continue = Invalid text: %text% on line %line%. Do you want to continue? + +#after version 4.0.5 +contextmenu.copyTag = Copy tag to +fit = fit +button.setAdvanceValues = Set advance values + +menu.tools.replace = Text Replace + +message.confirm.close = There are unsaved changes. Do you really want to close {swfName}? +message.confirm.closeAll = There are unsaved changes. Do you really want to close all SWFs? + +contextmenu.exportJavaSource = Export Java Source +contextmenu.exportSwfXml = Export SWF as XML +contextmenu.importSwfXml = Import SWF XML + +filter.xml = XML + +#after version 4.1.0 +contextmenu.undo = Undo + +text.align.left = Left align +text.align.right = Right align +text.align.center = Center align +text.align.justify = Justify align + +text.undo = Undo changes + +menu.file.import.xml = Import SWF XML +menu.file.export.xml = Export SWF XML + +#after version 4.1.1 +text.align.translatex.decrease = Decrease TranslateX +text.align.translatex.increase = Increase TranslateX +selectPreviousTag = Select previous tag +selectNextTag = Select next tag +button.ignoreAll = Ignore All +menu.file.import.symbolClass = Import Symbol-Class +text.toggleCase = Toggle case + +#after version 5.0.2 +preview.loop = Loop +menu.file.import.script = Import script +contextmenu.copyTagWithDependencies = Copy tag with dependencies to +button.replaceWithTag = Replace with other character tag +button.resolveConstants = Resolve constants + +#after version 5.1.0 +button.viewConstants = View Constants +work.exported = Exported +button.replaceAlphaChannel = Replace alpha channel... + +tagInfo.header.name = Name +tagInfo.header.value = Value +tagInfo.tagType = Tag Type +tagInfo.characterId = Character Id +tagInfo.offset = Offset +tagInfo.length = Length +tagInfo.bounds = Bounds +tagInfo.width = Width +tagInfo.height = Height +tagInfo.neededCharacters = Needed Characters + +button.viewhexpcode = View Hex with instructions +taginfo.header = Basic tag info + +tagInfo.dependentCharacters = Dependent Characters + +#after version 5.3.0 +header.uncompressed = Uncompressed +header.warning.unsupportedGfxCompression = GFX supports only uncompressed or Zlib compressed content. +header.warning.minimumZlibVersion = Zlib compression needs SWF version 6 or greater. +header.warning.minimumLzmaVersion = LZMA compression needs SWF version 13 or greater. + +tagInfo.codecName = Codec Name +tagInfo.exportFormat = Export Format +tagInfo.samplingRate = Sampling Rate +tagInfo.stereo = Stereo +tagInfo.sampleCount = Sample Count + +filter.dmg = Mac Executable files (*.dmg) +filter.linuxExe = Linux Executable files + +import.script.result = %count% scripts imported. +import.script.as12warning = Import script can import only AS1/2 scripts. + +error.constantPoolTooBig = Constant pool is too big. index=%index%, size=%size% +error.image.alpha.invalid = Invalid alpha channel data. + +#after version 6.0.2 +contextmenu.saveUncompressedToFile = Save to Uncompressed File +abc.traitslist.scriptinitializer = script initializer +menu.settings.autoOpenLoadedSWFs = Open loaded SWFs while playing + +#after version 6.1.1 +menu.file.start = Start +menu.file.start.run = Run +menu.file.start.stop = Stop +menu.file.start.debug = Debug +menu.debugging = Debugging +menu.debugging.debug = Debug +menu.debugging.debug.stop = Stop +menu.debugging.debug.pause = Pause +menu.debugging.debug.stepOver = Step over +menu.debugging.debug.stepInto = Step into +menu.debugging.debug.stepOut = Step out +menu.debugging.debug.continue = Continue +menu.debugging.debug.stack = Stack... +menu.debugging.debug.watch = New watch... + +message.playerpath.notset = Flash Player projector not found. Please configure its path in Advanced Settings / Paths (1). +message.playerpath.debug.notset = Flash Player projector content debugger not found. Please configure its path in Advanced Settings / Paths (2). +message.playerpath.lib.notset = PlayerGlobal (.SWC) not found. Please configure its path in Advanced Settings / Paths (3). + +debugpanel.header = Debugging + +variables.header.registers = Registers +variables.header.locals = Locals +variables.header.arguments = Arguments +variables.header.scopeChain = Scope chain +variables.column.name = Name +variables.column.type = Type +variables.column.value = Value + +callStack.header = Call stack +callStack.header.file = File +callStack.header.line = Line + +stack.header = Stack +stack.header.item = Item + +constantpool.header = Constant pool +constantpool.header.id = Id +constantpool.header.value = Value + +work.running = Running +work.debugging = Debugging +work.debugging.instrumenting = Preparing SWF for debugging +work.breakat = Break at\u0020 +work.halted = Debugging started, execution halted. Add breakpoints and click Continue (F5) to resume running. + +debuglog.header = Log +debuglog.button.clear = Clear + +#after 7.0.1 +work.debugging.wait = Waiting for Flash debug projector to connect + +error.debug.listen = Cannot listen on port %port%. There might be other flash debugger running. + +debug.break.reason.unknown = (Unknown) +debug.break.reason.breakpoint = (Breakpoint) +debug.break.reason.watch = (Watch) +debug.break.reason.fault = (Fault) +debug.break.reason.stopRequest = (Stop request) +debug.break.reason.step = (Step) +debug.break.reason.halt = (Halt) +debug.break.reason.scriptLoaded = (Script loaded) + +menu.file.start.debugpcode = Debug P-code + +#after 7.1.2 +button.replaceNoFill = Replace - Update bounds... +message.warning.svgImportExperimental = Not all SVG features are supported. Please check the log after import. + +message.imported.swf = The SWF file uses assets from an imported SWF file:\n%url%\nDo you want the assets to be loaded from that URL? +message.imported.swf.manually = Cannot load imported SWF\n%url%\nThe file or URL does not exist.\nDo you want to select local file? + +message.warning.hexViewNotUpToDate = Hex View is not up-to-date. Please save and reload the file to update Hex View. +message.font.replace.updateTexts = Some characters were replaced. Do you want to update the existing texts? + +menu.settings.simplifyExpressions = Simplify expressions + +#after 8.0.1 +menu.recentFiles.empty = Recent file list is empty +message.warning.outOfMemory32BitJre = OutOfMemory error occurred. You are running 32bit Java on 64bit system. Please use 64bit Java. + +menu.file.reloadAll = Reload all +message.confirm.reloadAll = This action cancels all unsaved changes in all SWF files and reloads whole application again.\nDo you want to continue? +export.script.singleFilePallelModeWarning = Single file script export is not supported with enabled parallel speedup + +button.showOriginalBytesInPcodeHex = Show original bytes +button.remove = Remove +button.showFileOffsetInPcodeHex = Show file offset + +generic.editor.amf3.title = AMF3 editor +generic.editor.amf3.help = AMF3 value syntax:\n\ + ------------------\n\ + scalar types:\n\ + %scalar_samples%\ + other types:\n\ + %nonscalar_samples%\ + \n\ + Notes:\n\ + \ * Nonscalar datatypes can be referenced by previously declared "id" attributes with # syntax:\n\ + %reference_sample%\n\ + \ * Keys in Dictionary entries can be any type\n +contextmenu.showInResources = Show in Resources \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/pipes/PipeInputStream.java b/src/com/jpexs/decompiler/flash/gui/pipes/PipeInputStream.java index 86031bb80..8226d0b85 100644 --- a/src/com/jpexs/decompiler/flash/gui/pipes/PipeInputStream.java +++ b/src/com/jpexs/decompiler/flash/gui/pipes/PipeInputStream.java @@ -1,97 +1,97 @@ -/* - * Copyright (C) 2010-2016 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.pipes; - -import com.sun.jna.Platform; -import com.sun.jna.platform.win32.Kernel32; -import com.sun.jna.platform.win32.WinNT.HANDLE; -import com.sun.jna.ptr.IntByReference; -import java.io.IOException; -import java.io.InputStream; - -/** - * - * @author JPEXS - */ -public class PipeInputStream extends InputStream { - - protected HANDLE pipe; - - private boolean closed = false; - - public PipeInputStream(String pipeName, boolean newpipe) throws IOException { - if (!Platform.isWindows()) { - throw new IOException("Cannot create Pipe on nonWindows OS"); - } - String fullPipePath = "\\\\.\\pipe\\" + pipeName; - if (newpipe) { - pipe = Kernel32.INSTANCE.CreateNamedPipe(fullPipePath, Kernel32.PIPE_ACCESS_INBOUND, Kernel32.PIPE_TYPE_BYTE, 1, 4096, 4096, 0, null); - if (pipe == null || !Kernel32.INSTANCE.ConnectNamedPipe(pipe, null)) { - throw new IOException("Cannot connect to the pipe"); - } - } else { - pipe = Kernel32.INSTANCE.CreateFile(fullPipePath, Kernel32.GENERIC_READ, Kernel32.FILE_SHARE_READ, null, Kernel32.OPEN_EXISTING, Kernel32.FILE_ATTRIBUTE_NORMAL, null); - } - if (pipe == null) { - throw new IOException("Cannot connect to the pipe"); - } - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - try { - close(); - } catch (IOException ex) { - //ignore - } - } - - }); - } - - @Override - public synchronized void close() throws IOException { - if (!closed) { - Kernel32.INSTANCE.CloseHandle(pipe); - closed = true; - } - } - - @Override - public synchronized int read() throws IOException { - byte[] d = new byte[1]; - if (readPipe(d) == 0) { - return -1; - } - return d[0]; - } - - private int readPipe(byte res[]) throws IOException { - final IntByReference ibr = new IntByReference(); - int read = 0; - while (read < res.length) { - byte[] data = new byte[res.length - read]; - boolean result = Kernel32.INSTANCE.ReadFile(pipe, data, data.length, ibr, null); - if (!result) { - throw new IOException("Cannot read pipe. Error " + Kernel32.INSTANCE.GetLastError()); - } - int readNow = ibr.getValue(); - System.arraycopy(data, 0, res, read, readNow); - read += readNow; - } - return read; - } -} +/* + * Copyright (C) 2010-2016 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.pipes; + +import com.sun.jna.Platform; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.WinNT.HANDLE; +import com.sun.jna.ptr.IntByReference; +import java.io.IOException; +import java.io.InputStream; + +/** + * + * @author JPEXS + */ +public class PipeInputStream extends InputStream { + + protected HANDLE pipe; + + private boolean closed = false; + + public PipeInputStream(String pipeName, boolean newpipe) throws IOException { + if (!Platform.isWindows()) { + throw new IOException("Cannot create Pipe on nonWindows OS"); + } + String fullPipePath = "\\\\.\\pipe\\" + pipeName; + if (newpipe) { + pipe = Kernel32.INSTANCE.CreateNamedPipe(fullPipePath, Kernel32.PIPE_ACCESS_INBOUND, Kernel32.PIPE_TYPE_BYTE, 1, 4096, 4096, 0, null); + if (pipe == null || !Kernel32.INSTANCE.ConnectNamedPipe(pipe, null)) { + throw new IOException("Cannot connect to the pipe"); + } + } else { + pipe = Kernel32.INSTANCE.CreateFile(fullPipePath, Kernel32.GENERIC_READ, Kernel32.FILE_SHARE_READ, null, Kernel32.OPEN_EXISTING, Kernel32.FILE_ATTRIBUTE_NORMAL, null); + } + if (pipe == null) { + throw new IOException("Cannot connect to the pipe"); + } + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + try { + close(); + } catch (IOException ex) { + //ignore + } + } + + }); + } + + @Override + public synchronized void close() throws IOException { + if (!closed) { + Kernel32.INSTANCE.CloseHandle(pipe); + closed = true; + } + } + + @Override + public synchronized int read() throws IOException { + byte[] d = new byte[1]; + if (readPipe(d) == 0) { + return -1; + } + return d[0]; + } + + private int readPipe(byte res[]) throws IOException { + final IntByReference ibr = new IntByReference(); + int read = 0; + while (read < res.length) { + byte[] data = new byte[res.length - read]; + boolean result = Kernel32.INSTANCE.ReadFile(pipe, data, data.length, ibr, null); + if (!result) { + throw new IOException("Cannot read pipe. Error " + Kernel32.INSTANCE.GetLastError()); + } + int readNow = ibr.getValue(); + System.arraycopy(data, 0, res, read, readNow); + read += readNow; + } + return read; + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/pipes/PipeOutputStream.java b/src/com/jpexs/decompiler/flash/gui/pipes/PipeOutputStream.java index 40c0774c5..9f996cad1 100644 --- a/src/com/jpexs/decompiler/flash/gui/pipes/PipeOutputStream.java +++ b/src/com/jpexs/decompiler/flash/gui/pipes/PipeOutputStream.java @@ -1,85 +1,85 @@ -/* - * Copyright (C) 2010-2016 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.pipes; - -import com.sun.jna.Platform; -import com.sun.jna.platform.win32.Kernel32; -import com.sun.jna.platform.win32.WinNT.HANDLE; -import com.sun.jna.ptr.IntByReference; -import java.io.IOException; -import java.io.OutputStream; - -/** - * - * @author JPEXS - */ -public class PipeOutputStream extends OutputStream { - - protected HANDLE pipe; - - private boolean closed = false; - - public PipeOutputStream(String pipeName, boolean newPipe) throws IOException { - if (!Platform.isWindows()) { - throw new IOException("Cannot create Pipe on nonWindows OS"); - } - String fullPipePath = "\\\\.\\pipe\\" + pipeName; - if (newPipe) { - pipe = Kernel32.INSTANCE.CreateNamedPipe(fullPipePath, Kernel32.PIPE_ACCESS_OUTBOUND, Kernel32.PIPE_TYPE_BYTE, 1, 4096, 4096, 0, null); - if (pipe == null || !Kernel32.INSTANCE.ConnectNamedPipe(pipe, null)) { - throw new IOException("Cannot connect to the pipe. Error " + Kernel32.INSTANCE.GetLastError()); - } - } else { - pipe = Kernel32.INSTANCE.CreateFile(fullPipePath, Kernel32.GENERIC_WRITE, Kernel32.FILE_SHARE_WRITE, null, Kernel32.OPEN_EXISTING, Kernel32.FILE_ATTRIBUTE_NORMAL, null); - if (pipe == null) { - throw new IOException("Cannot connect to the pipe. Error " + Kernel32.INSTANCE.GetLastError()); - } - } - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - try { - close(); - } catch (IOException ex) { - //ignore - } - } - - }); - } - - @Override - public synchronized void close() throws IOException { - if (!closed) { - Kernel32.INSTANCE.CloseHandle(pipe); - closed = true; - } - } - - @Override - public synchronized void write(int b) throws IOException { - byte[] data = new byte[]{(byte) b}; - IntByReference ibr = new IntByReference(); - boolean result = Kernel32.INSTANCE.WriteFile(pipe, data, data.length, ibr, null); - if (!result) { - throw new IOException("Cannot write to the pipe. Error " + Kernel32.INSTANCE.GetLastError()); - } - if (ibr.getValue() != data.length) { - throw new IOException("Cannot write to the pipe. Error " + Kernel32.INSTANCE.GetLastError()); - } - } -} +/* + * Copyright (C) 2010-2016 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.pipes; + +import com.sun.jna.Platform; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.WinNT.HANDLE; +import com.sun.jna.ptr.IntByReference; +import java.io.IOException; +import java.io.OutputStream; + +/** + * + * @author JPEXS + */ +public class PipeOutputStream extends OutputStream { + + protected HANDLE pipe; + + private boolean closed = false; + + public PipeOutputStream(String pipeName, boolean newPipe) throws IOException { + if (!Platform.isWindows()) { + throw new IOException("Cannot create Pipe on nonWindows OS"); + } + String fullPipePath = "\\\\.\\pipe\\" + pipeName; + if (newPipe) { + pipe = Kernel32.INSTANCE.CreateNamedPipe(fullPipePath, Kernel32.PIPE_ACCESS_OUTBOUND, Kernel32.PIPE_TYPE_BYTE, 1, 4096, 4096, 0, null); + if (pipe == null || !Kernel32.INSTANCE.ConnectNamedPipe(pipe, null)) { + throw new IOException("Cannot connect to the pipe. Error " + Kernel32.INSTANCE.GetLastError()); + } + } else { + pipe = Kernel32.INSTANCE.CreateFile(fullPipePath, Kernel32.GENERIC_WRITE, Kernel32.FILE_SHARE_WRITE, null, Kernel32.OPEN_EXISTING, Kernel32.FILE_ATTRIBUTE_NORMAL, null); + if (pipe == null) { + throw new IOException("Cannot connect to the pipe. Error " + Kernel32.INSTANCE.GetLastError()); + } + } + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + try { + close(); + } catch (IOException ex) { + //ignore + } + } + + }); + } + + @Override + public synchronized void close() throws IOException { + if (!closed) { + Kernel32.INSTANCE.CloseHandle(pipe); + closed = true; + } + } + + @Override + public synchronized void write(int b) throws IOException { + byte[] data = new byte[]{(byte) b}; + IntByReference ibr = new IntByReference(); + boolean result = Kernel32.INSTANCE.WriteFile(pipe, data, data.length, ibr, null); + if (!result) { + throw new IOException("Cannot write to the pipe. Error " + Kernel32.INSTANCE.GetLastError()); + } + if (ibr.getValue() != data.length) { + throw new IOException("Cannot write to the pipe. Error " + Kernel32.INSTANCE.GetLastError()); + } + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/proxy/ProxyFrame.java b/src/com/jpexs/decompiler/flash/gui/proxy/ProxyFrame.java index c121d2b6b..77a86e3ee 100644 --- a/src/com/jpexs/decompiler/flash/gui/proxy/ProxyFrame.java +++ b/src/com/jpexs/decompiler/flash/gui/proxy/ProxyFrame.java @@ -1,759 +1,759 @@ -/* - * Copyright (C) 2010-2016 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.proxy; - -import com.jpexs.decompiler.flash.RetryTask; -import com.jpexs.decompiler.flash.RunnableIOEx; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.gui.AppFrame; -import com.jpexs.decompiler.flash.gui.AppStrings; -import com.jpexs.decompiler.flash.gui.GuiAbortRetryIgnoreHandler; -import com.jpexs.decompiler.flash.gui.Main; -import com.jpexs.decompiler.flash.gui.MainFrame; -import com.jpexs.decompiler.flash.gui.View; -import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.utf8.Utf8InputStreamReader; -import com.jpexs.helpers.utf8.Utf8OutputStreamWriter; -import com.jpexs.proxy.CatchedListener; -import com.jpexs.proxy.ReplacedListener; -import com.jpexs.proxy.Replacement; -import com.jpexs.proxy.Server; -import java.awt.BorderLayout; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.Image; -import java.awt.Toolkit; -import java.awt.datatransfer.Clipboard; -import java.awt.datatransfer.StringSelection; -import java.awt.event.ActionEvent; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.JTextField; -import javax.swing.SwingConstants; -import javax.swing.filechooser.FileFilter; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableColumnModel; -import javax.swing.table.DefaultTableModel; - -/** - * Frame with Proxy - * - * @author JPEXS - */ -public class ProxyFrame extends AppFrame implements CatchedListener, MouseListener, ReplacedListener { - - private static final String REPLACEMENTS_NAME = "replacements.cfg"; - - private JTable replacementsTable; - - private JButton switchButton = new JButton(translate("proxy.start")); - - private boolean started = false; - - private JTextField portField = new JTextField("55555"); - - private JCheckBox sniffSWFCheckBox = new JCheckBox("SWF", false); - - private JCheckBox sniffOSCheckBox = new JCheckBox("OctetStream", false); - - private JCheckBox sniffJSCheckBox = new JCheckBox("JS", false); - - private JCheckBox sniffXMLCheckBox = new JCheckBox("XML", false); - - /** - * Is server running - * - * @return True when running - */ - public boolean isRunning() { - return started; - } - - /** - * Sets port for the proxy - * - * @param port Port number - */ - public void setPort(int port) { - portField.setText(Integer.toString(port)); - } - - private static class SizeItem implements Comparable { - - String file; - - public SizeItem(String file) { - this.file = file; - } - - @Override - public String toString() { - return Helper.byteCountStr(new File(file).length(), false); - } - - @Override - public int compareTo(SizeItem o) { - return (int) (new File(file).length() - new File(o.file).length()); - } - } - - DefaultTableModel tableModel; - - private SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss"); - - /** - * List of replacements - */ - private static List replacements = new ArrayList<>(); - - /** - * Saves replacements to file for future use - */ - private static void saveReplacements() { - String replacementsFile = getReplacementsFile(); - if (replacements.isEmpty()) { - File rf = new File(replacementsFile); - if (rf.exists()) { - if (!rf.delete()) { - Logger.getLogger(ProxyFrame.class.getName()).log(Level.SEVERE, "Cannot delete replacements file"); - } - } - } else { - try (PrintWriter pw = new PrintWriter(new Utf8OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(replacementsFile))))) { - for (Replacement r : replacements) { - pw.println(r.urlPattern); - pw.println(r.targetFile); - } - } catch (IOException ex) { - Logger.getLogger(ProxyFrame.class.getName()).log(Level.SEVERE, "Exception during saving replacements", ex); - } - } - } - - /** - * Load replacements from file - */ - private static void loadReplacements() { - String replacementsFile = getReplacementsFile(); - if (!(new File(replacementsFile)).exists()) { - return; - } - replacements = new ArrayList<>(); - try (BufferedReader br = new BufferedReader(new Utf8InputStreamReader(new FileInputStream(replacementsFile)))) { - String s; - while ((s = br.readLine()) != null) { - Replacement r = new Replacement(s, br.readLine()); - replacements.add(r); - } - } catch (IOException e) { - //ignore - } - } - - private static String getReplacementsFile() { - return Configuration.getFFDecHome() + REPLACEMENTS_NAME; - } - - /** - * Constructor - * - * @param mainFrame Main frame - */ - public ProxyFrame(final MainFrame mainFrame) { - - final String[] columnNames = new String[]{ - translate("column.accessed"), - translate("column.size"), - translate("column.url")}; - - loadReplacements(); - - Object[][] data = new Object[replacements.size()][3]; - - for (int i = 0; i < replacements.size(); i++) { - Replacement r = replacements.get(i); - data[i][0] = r.lastAccess == null ? "" : format.format(r.lastAccess.getTime()); - data[i][1] = new SizeItem(r.targetFile); - data[i][2] = r.urlPattern; - } - - tableModel = new DefaultTableModel(data, columnNames) { - - @Override - public Class getColumnClass(int columnIndex) { - Class[] classes = new Class[]{String.class, SizeItem.class, String.class}; - return classes[columnIndex]; - } - - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - - }; - replacementsTable = new JTable(tableModel); - - DefaultTableCellRenderer tcr = new DefaultTableCellRenderer(); - tcr.setHorizontalAlignment(SwingConstants.RIGHT); - - replacementsTable.setDefaultRenderer(String.class, new DefaultTableCellRenderer()); - replacementsTable.setDefaultRenderer(SizeItem.class, tcr); - - replacementsTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); - - replacementsTable.setRowSelectionAllowed(true); - - DefaultTableColumnModel colModel = (DefaultTableColumnModel) replacementsTable.getColumnModel(); - colModel.getColumn(0).setMaxWidth(100); - - colModel.getColumn(1).setMaxWidth(200); - - replacementsTable.setAutoCreateRowSorter(true); - - replacementsTable.setAutoCreateRowSorter(false); - - replacementsTable.addMouseListener(this); - replacementsTable.setFont(new Font("Monospaced", Font.PLAIN, 12)); - switchButton.addActionListener(this::switchStateButtonActionPerformed); - Container cnt = getContentPane(); - cnt.setLayout(new BorderLayout()); - cnt.add(new JScrollPane(replacementsTable), BorderLayout.CENTER); - - portField.setPreferredSize(new Dimension(80, portField.getPreferredSize().height)); - JPanel buttonsPanel = new JPanel(); - buttonsPanel.setLayout(new FlowLayout()); - buttonsPanel.add(new JLabel(translate("port"))); - buttonsPanel.add(portField); - buttonsPanel.add(switchButton); - cnt.add(buttonsPanel, BorderLayout.NORTH); - - JPanel buttonsPanel23 = new JPanel(); - buttonsPanel23.setLayout(new BoxLayout(buttonsPanel23, BoxLayout.Y_AXIS)); - - JPanel buttonsPanel21 = new JPanel(new FlowLayout()); - JButton openButton = new JButton(translate("open")); - openButton.addActionListener(this::openButtonActionPerformed); - buttonsPanel21.add(openButton); - JButton clearButton = new JButton(translate("clear")); - clearButton.addActionListener(this::clearButtonActionPerformed); - buttonsPanel21.add(clearButton); - JButton renameButton = new JButton(translate("rename")); - renameButton.addActionListener(this::renameButtonActionPerformed); - buttonsPanel21.add(renameButton); - JButton removeButton = new JButton(translate("remove")); - removeButton.addActionListener(this::removeButtonActionPerformed); - buttonsPanel21.add(removeButton); - - //JPanel buttonsPanel22 = new JPanel(new FlowLayout()); - JButton copyUrlButton = new JButton(translate("copy.url")); - copyUrlButton.addActionListener(this::copyUrlButtonActionPerformed); - buttonsPanel21.add(copyUrlButton); - - JButton saveAsButton = new JButton(translate("save.as")); - saveAsButton.addActionListener(this::saveAsButtonActionPerformed); - buttonsPanel21.add(saveAsButton); - - JButton replaceButton = new JButton(translate("replace")); - replaceButton.addActionListener(this::replaceButtonActionPerformed); - buttonsPanel21.add(replaceButton); - - JPanel buttonsPanel3 = new JPanel(); - buttonsPanel3.setLayout(new FlowLayout()); - buttonsPanel3.add(new JLabel(translate("sniff"))); - buttonsPanel3.add(sniffSWFCheckBox); - buttonsPanel3.add(sniffOSCheckBox); - //buttonsPanel3.add(sniffJSCheckBox); - //buttonsPanel3.add(sniffXMLCheckBox); - - buttonsPanel23.add(buttonsPanel21); - //buttonsPanel23.add(buttonsPanel22); - buttonsPanel23.add(buttonsPanel3); - - cnt.add(buttonsPanel23, BorderLayout.SOUTH); - setSize(800, 500); - View.centerScreen(this); - View.setWindowIcon(this); - setTitle(translate("dialog.title")); - this.addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - setVisible(false); - Main.removeTrayIcon(); - if (mainFrame != null) { - if (mainFrame.isVisible()) { - return; - } - } - Main.showModeFrame(); - } - - /** - * Invoked when a window is iconified. - */ - @Override - public void windowIconified(WindowEvent e) { - setVisible(false); - } - }); - List images = new ArrayList<>(); - images.add(View.loadImage("proxy16")); - images.add(View.loadImage("proxy32")); - setIconImages(images); - } - - private void open() { - if (replacementsTable.getSelectedRow() > -1) { - Replacement r = replacements.get(replacementsTable.getRowSorter().convertRowIndexToModel(replacementsTable.getSelectedRow())); - Main.openFile(r.targetFile, r.urlPattern); - } - } - - 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; - } - - private int[] getSelectedRows() { - int[] sel = replacementsTable.getSelectedRows(); - for (int i = 0; i < sel.length; i++) { - sel[i] = replacementsTable.getRowSorter().convertRowIndexToModel(sel[i]); - } - - return sel; - } - - private void openButtonActionPerformed(ActionEvent evt) { - open(); - } - - private void saveAsButtonActionPerformed(ActionEvent evt) { - int[] sel = getSelectedRows(); - if (sel.length == 1) { - Replacement r = replacements.get(sel[0]); - JFileChooser fc = new JFileChooser(); - fc.setCurrentDirectory(new File(Configuration.lastSaveDir.get())); - String n = r.urlPattern; - if (n.contains("?")) { - n = n.substring(0, n.indexOf('?')); - } - if (n.contains("/")) { - n = n.substring(n.lastIndexOf('/')); - } - n = Helper.makeFileName(n); - fc.setSelectedFile(new File(Configuration.lastSaveDir.get(), n)); - String ext = ".swf"; - final String extension = ext; - FileFilter swfFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(extension)) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter" + extension); - } - }; - fc.setFileFilter(swfFilter); - fc.setAcceptAllFileFilterUsed(true); - JFrame f = new JFrame(); - View.setWindowIcon(f); - if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) { - File file = Helper.fixDialogFile(fc.getSelectedFile()); - try { - Files.copy(new File(r.targetFile).toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); - } catch (IOException ex) { - View.showMessageDialog(this, translate("error.save.as") + "\r\n" + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - } - } else { - GuiAbortRetryIgnoreHandler handler = new GuiAbortRetryIgnoreHandler(); - File exportDir = new File(selectExportDir()); - for (int s : sel) { - final Replacement r = replacements.get(s); - String n = r.urlPattern; - if (n.contains("?")) { - n = n.substring(0, n.indexOf('?')); - } - if (n.contains("/")) { - n = n.substring(n.lastIndexOf('/')); - } - n = Helper.makeFileName(n); - int c = 2; - String n2 = n; - while (new File(exportDir, n2).exists()) { - if (n.contains(".")) { - n2 = n.substring(0, n.lastIndexOf('.')) + c + n.substring(n.lastIndexOf('.')); - c++; - } else { - n2 = n + c + ".swf"; - c++; - } - } - - final File outfile = new File(exportDir, n2); - try { - new RetryTask(new RunnableIOEx() { - @Override - public void run() throws IOException { - Files.copy(new File(r.targetFile).toPath(), outfile.toPath(), StandardCopyOption.REPLACE_EXISTING); - } - }, handler).run(); - } catch (IOException | InterruptedException ex) { - break; - } - } - } - } - - private void replaceButtonActionPerformed(ActionEvent evt) { - int[] sel = getSelectedRows(); - if (sel.length > 0) { - Replacement r = replacements.get(sel[0]); - JFileChooser fc = new JFileChooser(); - fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); - String ext = ".swf"; - final String extension = ext; - FileFilter swfFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(extension)) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter" + extension); - } - }; - fc.setFileFilter(swfFilter); - fc.setAcceptAllFileFilterUsed(true); - JFrame f = new JFrame(); - View.setWindowIcon(f); - if (fc.showOpenDialog(f) == JFileChooser.APPROVE_OPTION) { - File file = Helper.fixDialogFile(fc.getSelectedFile()); - try { - Files.copy(file.toPath(), new File(r.targetFile).toPath(), StandardCopyOption.REPLACE_EXISTING); - tableModel.fireTableCellUpdated(sel[0], 1/*size*/); - } catch (IOException ex) { - View.showMessageDialog(f, translate("error.replace") + "\r\n" + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - } - } - } - - private void copyUrlButtonActionPerformed(ActionEvent evt) { - int[] sel = getSelectedRows(); - StringBuilder copyText = new StringBuilder(); - for (int sc : sel) { - Replacement r = replacements.get(sc); - if (copyText.length() > 0) { - copyText.append(System.lineSeparator()); - } - copyText.append(r.urlPattern); - } - - if (copyText.length() > 0) { - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - StringSelection stringSelection = new StringSelection(copyText.toString()); - clipboard.setContents(stringSelection, null); - } - } - - private void renameButtonActionPerformed(ActionEvent evt) { - int[] sel = getSelectedRows(); - if (sel.length > 0) { - Replacement r = replacements.get(sel[0]); - String s = View.showInputDialog("URL", r.urlPattern); - if (s != null) { - r.urlPattern = s; - tableModel.setValueAt(s, sel[0], 2/*url*/); - } - } - } - - private void clearButtonActionPerformed(ActionEvent evt) { - for (Replacement r : replacements) { - File f; - try { - f = (new File(Main.tempFile(r.targetFile))); - if (f.exists()) { - f.delete(); - } - } catch (IOException ex) { - Logger.getLogger(ProxyFrame.class.getName()).log(Level.SEVERE, null, ex); - } - } - tableModel.setRowCount(0); - replacements.clear(); - saveReplacements(); - } - - private void removeButtonActionPerformed(ActionEvent evt) { - int[] sel = getSelectedRows(); - Arrays.sort(sel); - for (int i = sel.length - 1; i >= 0; i--) { - tableModel.removeRow(sel[i]); - Replacement r = replacements.remove(sel[i]); - saveReplacements(); - File f = (new File(r.targetFile)); - if (f.exists()) { - f.delete(); - } - } - } - - private void switchStateButtonActionPerformed(ActionEvent evt) { - Main.switchProxy(); - } - - /** - * Switch proxy state - */ - public void switchState() { - started = !started; - if (started) { - int port = 0; - try { - port = Integer.parseInt(portField.getText()); - } catch (NumberFormatException nfe) { - } - if ((port <= 0) || (port > 65535)) { - View.showMessageDialog(this, translate("error.port"), translate("error"), JOptionPane.ERROR_MESSAGE); - started = false; - return; - } - List catchedContentTypes = new ArrayList<>(); - catchedContentTypes.add("application/x-shockwave-flash"); - catchedContentTypes.add("application/x-javascript"); - catchedContentTypes.add("application/javascript"); - catchedContentTypes.add("text/javascript"); - catchedContentTypes.add("application/json"); - catchedContentTypes.add("text/xml"); - catchedContentTypes.add("application/xml"); - catchedContentTypes.add("application/octet-stream"); - if (!Server.startServer(port, replacements, catchedContentTypes, this, this)) { - JOptionPane.showMessageDialog(this, translate("error.start.server").replace("%port%", "" + port), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - started = false; - return; - } - switchButton.setText(translate("proxy.stop")); - portField.setEditable(false); - } else { - Server.stopServer(); - switchButton.setText(translate("proxy.start")); - portField.setEditable(true); - } - } - - /** - * Mouse clicked event - * - * @param e event - */ - @Override - public void mouseClicked(MouseEvent e) { - if (e.getSource() == replacementsTable) { - if (e.getClickCount() == 2) { - open(); - } - } - } - - /** - * Mouse pressed event - * - * @param e event - */ - @Override - public void mousePressed(MouseEvent e) { - } - - /** - * Mouse released event - * - * @param e event - */ - @Override - public void mouseReleased(MouseEvent e) { - } - - /** - * Mouse entered event - * - * @param e event - */ - @Override - public void mouseEntered(MouseEvent e) { - } - - /** - * Mouse exited event - * - * @param e event - */ - @Override - public void mouseExited(MouseEvent e) { - } - - /** - * Method called when specified contentType is received - * - * @param contentType Content type - * @param url URL of the method - * @param data Data stream - * @return replacement data - */ - @Override - public byte[] catched(String contentType, String url, InputStream data) { - boolean swfOnly = false; - if (contentType.contains(";")) { - contentType = contentType.substring(0, contentType.indexOf(';')); - } - if ((!sniffSWFCheckBox.isSelected()) && (contentType.equals("application/x-shockwave-flash"))) { - return null; - } - if ((!sniffJSCheckBox.isSelected()) && (contentType.equals("application/javascript") || contentType.equals("application/x-javascript") || contentType.equals("text/javascript") || contentType.equals("application/json"))) { - return null; - } - if ((!sniffXMLCheckBox.isSelected()) && (contentType.equals("application/xml") || contentType.equals("text/xml"))) { - return null; - } - if ((!sniffOSCheckBox.isSelected()) && (contentType.equals("application/octet-stream"))) { - return null; - } - - byte[] result = null; - - boolean cont = false; - for (Replacement r : replacements) { - if (r.matches(url)) { - cont = true; - break; - } - } - if (!cont) { - try { - byte[] hdr = new byte[3]; - if (data.read(hdr) != 3) { - throw new IOException(); - } - - String shdr = new String(hdr); - if (swfOnly && ((!shdr.equals("FWS")) && (!shdr.equals("CWS")) && (!shdr.equals("ZWS")))) { - return null; //NOT SWF - } - - String tempFilePath = Main.tempFile(url); - data.reset(); - byte[] dataArray = Helper.readStream(data); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(new File(tempFilePath)))) { - fos.write(dataArray); - } - - result = SWFDecompilerPlugin.fireProxyFileCatched(dataArray); - - Replacement r = new Replacement(url, tempFilePath); - r.lastAccess = Calendar.getInstance(); - replacements.add(r); - saveReplacements(); - tableModel.addRow(new Object[]{ - r.lastAccess == null ? "" : format.format(r.lastAccess.getTime()), - new SizeItem(r.targetFile), - r.urlPattern - }); - } catch (IOException e) { - } - } - - return result; - } - - /** - * Shows or hides this {@code Window} depending on the value of parameter - * {@code b}. - * - * @param b if {@code true}, makes the {@code Window} visible, otherwise - * hides the {@code Window}. If the {@code Window} and/or its owner are not - * yet displayable, both are made displayable. The {@code Window} will be - * validated prior to being made visible. If the {@code Window} is already - * visible, this will bring the {@code Window} to the front.

- * If {@code false}, hides this {@code Window}, its subcomponents, and all - * of its owned children. The {@code Window} and its subcomponents can be - * made visible again with a call to {@code #setVisible(true)}. - * @see java.awt.Component#isDisplayable - * @see java.awt.Component#setVisible - * @see java.awt.Window#toFront - * @see java.awt.Window#dispose - */ - @Override - public void setVisible(boolean b) { - if (b == true) { - Main.addTrayIcon(); - } - super.setVisible(b); - } - - @Override - public void replaced(Replacement replacement, String url, String contentType) { - int index = replacements.indexOf(replacement); - tableModel.setValueAt(replacement.lastAccess == null ? "" : format.format(replacement.lastAccess.getTime()), index, 0); - tableModel.setValueAt(new SizeItem(replacement.targetFile), index, 1); - tableModel.setValueAt(replacement.urlPattern, index, 2); - } -} +/* + * Copyright (C) 2010-2016 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.proxy; + +import com.jpexs.decompiler.flash.RetryTask; +import com.jpexs.decompiler.flash.RunnableIOEx; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.gui.AppFrame; +import com.jpexs.decompiler.flash.gui.AppStrings; +import com.jpexs.decompiler.flash.gui.GuiAbortRetryIgnoreHandler; +import com.jpexs.decompiler.flash.gui.Main; +import com.jpexs.decompiler.flash.gui.MainFrame; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.utf8.Utf8InputStreamReader; +import com.jpexs.helpers.utf8.Utf8OutputStreamWriter; +import com.jpexs.proxy.CatchedListener; +import com.jpexs.proxy.ReplacedListener; +import com.jpexs.proxy.Replacement; +import com.jpexs.proxy.Server; +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Image; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; +import java.awt.event.ActionEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.SwingConstants; +import javax.swing.filechooser.FileFilter; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.DefaultTableModel; + +/** + * Frame with Proxy + * + * @author JPEXS + */ +public class ProxyFrame extends AppFrame implements CatchedListener, MouseListener, ReplacedListener { + + private static final String REPLACEMENTS_NAME = "replacements.cfg"; + + private JTable replacementsTable; + + private JButton switchButton = new JButton(translate("proxy.start")); + + private boolean started = false; + + private JTextField portField = new JTextField("55555"); + + private JCheckBox sniffSWFCheckBox = new JCheckBox("SWF", false); + + private JCheckBox sniffOSCheckBox = new JCheckBox("OctetStream", false); + + private JCheckBox sniffJSCheckBox = new JCheckBox("JS", false); + + private JCheckBox sniffXMLCheckBox = new JCheckBox("XML", false); + + /** + * Is server running + * + * @return True when running + */ + public boolean isRunning() { + return started; + } + + /** + * Sets port for the proxy + * + * @param port Port number + */ + public void setPort(int port) { + portField.setText(Integer.toString(port)); + } + + private static class SizeItem implements Comparable { + + String file; + + public SizeItem(String file) { + this.file = file; + } + + @Override + public String toString() { + return Helper.byteCountStr(new File(file).length(), false); + } + + @Override + public int compareTo(SizeItem o) { + return (int) (new File(file).length() - new File(o.file).length()); + } + } + + DefaultTableModel tableModel; + + private SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss"); + + /** + * List of replacements + */ + private static List replacements = new ArrayList<>(); + + /** + * Saves replacements to file for future use + */ + private static void saveReplacements() { + String replacementsFile = getReplacementsFile(); + if (replacements.isEmpty()) { + File rf = new File(replacementsFile); + if (rf.exists()) { + if (!rf.delete()) { + Logger.getLogger(ProxyFrame.class.getName()).log(Level.SEVERE, "Cannot delete replacements file"); + } + } + } else { + try (PrintWriter pw = new PrintWriter(new Utf8OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(replacementsFile))))) { + for (Replacement r : replacements) { + pw.println(r.urlPattern); + pw.println(r.targetFile); + } + } catch (IOException ex) { + Logger.getLogger(ProxyFrame.class.getName()).log(Level.SEVERE, "Exception during saving replacements", ex); + } + } + } + + /** + * Load replacements from file + */ + private static void loadReplacements() { + String replacementsFile = getReplacementsFile(); + if (!(new File(replacementsFile)).exists()) { + return; + } + replacements = new ArrayList<>(); + try (BufferedReader br = new BufferedReader(new Utf8InputStreamReader(new FileInputStream(replacementsFile)))) { + String s; + while ((s = br.readLine()) != null) { + Replacement r = new Replacement(s, br.readLine()); + replacements.add(r); + } + } catch (IOException e) { + //ignore + } + } + + private static String getReplacementsFile() { + return Configuration.getFFDecHome() + REPLACEMENTS_NAME; + } + + /** + * Constructor + * + * @param mainFrame Main frame + */ + public ProxyFrame(final MainFrame mainFrame) { + + final String[] columnNames = new String[]{ + translate("column.accessed"), + translate("column.size"), + translate("column.url")}; + + loadReplacements(); + + Object[][] data = new Object[replacements.size()][3]; + + for (int i = 0; i < replacements.size(); i++) { + Replacement r = replacements.get(i); + data[i][0] = r.lastAccess == null ? "" : format.format(r.lastAccess.getTime()); + data[i][1] = new SizeItem(r.targetFile); + data[i][2] = r.urlPattern; + } + + tableModel = new DefaultTableModel(data, columnNames) { + + @Override + public Class getColumnClass(int columnIndex) { + Class[] classes = new Class[]{String.class, SizeItem.class, String.class}; + return classes[columnIndex]; + } + + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + + }; + replacementsTable = new JTable(tableModel); + + DefaultTableCellRenderer tcr = new DefaultTableCellRenderer(); + tcr.setHorizontalAlignment(SwingConstants.RIGHT); + + replacementsTable.setDefaultRenderer(String.class, new DefaultTableCellRenderer()); + replacementsTable.setDefaultRenderer(SizeItem.class, tcr); + + replacementsTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); + + replacementsTable.setRowSelectionAllowed(true); + + DefaultTableColumnModel colModel = (DefaultTableColumnModel) replacementsTable.getColumnModel(); + colModel.getColumn(0).setMaxWidth(100); + + colModel.getColumn(1).setMaxWidth(200); + + replacementsTable.setAutoCreateRowSorter(true); + + replacementsTable.setAutoCreateRowSorter(false); + + replacementsTable.addMouseListener(this); + replacementsTable.setFont(new Font("Monospaced", Font.PLAIN, 12)); + switchButton.addActionListener(this::switchStateButtonActionPerformed); + Container cnt = getContentPane(); + cnt.setLayout(new BorderLayout()); + cnt.add(new JScrollPane(replacementsTable), BorderLayout.CENTER); + + portField.setPreferredSize(new Dimension(80, portField.getPreferredSize().height)); + JPanel buttonsPanel = new JPanel(); + buttonsPanel.setLayout(new FlowLayout()); + buttonsPanel.add(new JLabel(translate("port"))); + buttonsPanel.add(portField); + buttonsPanel.add(switchButton); + cnt.add(buttonsPanel, BorderLayout.NORTH); + + JPanel buttonsPanel23 = new JPanel(); + buttonsPanel23.setLayout(new BoxLayout(buttonsPanel23, BoxLayout.Y_AXIS)); + + JPanel buttonsPanel21 = new JPanel(new FlowLayout()); + JButton openButton = new JButton(translate("open")); + openButton.addActionListener(this::openButtonActionPerformed); + buttonsPanel21.add(openButton); + JButton clearButton = new JButton(translate("clear")); + clearButton.addActionListener(this::clearButtonActionPerformed); + buttonsPanel21.add(clearButton); + JButton renameButton = new JButton(translate("rename")); + renameButton.addActionListener(this::renameButtonActionPerformed); + buttonsPanel21.add(renameButton); + JButton removeButton = new JButton(translate("remove")); + removeButton.addActionListener(this::removeButtonActionPerformed); + buttonsPanel21.add(removeButton); + + //JPanel buttonsPanel22 = new JPanel(new FlowLayout()); + JButton copyUrlButton = new JButton(translate("copy.url")); + copyUrlButton.addActionListener(this::copyUrlButtonActionPerformed); + buttonsPanel21.add(copyUrlButton); + + JButton saveAsButton = new JButton(translate("save.as")); + saveAsButton.addActionListener(this::saveAsButtonActionPerformed); + buttonsPanel21.add(saveAsButton); + + JButton replaceButton = new JButton(translate("replace")); + replaceButton.addActionListener(this::replaceButtonActionPerformed); + buttonsPanel21.add(replaceButton); + + JPanel buttonsPanel3 = new JPanel(); + buttonsPanel3.setLayout(new FlowLayout()); + buttonsPanel3.add(new JLabel(translate("sniff"))); + buttonsPanel3.add(sniffSWFCheckBox); + buttonsPanel3.add(sniffOSCheckBox); + //buttonsPanel3.add(sniffJSCheckBox); + //buttonsPanel3.add(sniffXMLCheckBox); + + buttonsPanel23.add(buttonsPanel21); + //buttonsPanel23.add(buttonsPanel22); + buttonsPanel23.add(buttonsPanel3); + + cnt.add(buttonsPanel23, BorderLayout.SOUTH); + setSize(800, 500); + View.centerScreen(this); + View.setWindowIcon(this); + setTitle(translate("dialog.title")); + this.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + setVisible(false); + Main.removeTrayIcon(); + if (mainFrame != null) { + if (mainFrame.isVisible()) { + return; + } + } + Main.showModeFrame(); + } + + /** + * Invoked when a window is iconified. + */ + @Override + public void windowIconified(WindowEvent e) { + setVisible(false); + } + }); + List images = new ArrayList<>(); + images.add(View.loadImage("proxy16")); + images.add(View.loadImage("proxy32")); + setIconImages(images); + } + + private void open() { + if (replacementsTable.getSelectedRow() > -1) { + Replacement r = replacements.get(replacementsTable.getRowSorter().convertRowIndexToModel(replacementsTable.getSelectedRow())); + Main.openFile(r.targetFile, r.urlPattern); + } + } + + 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; + } + + private int[] getSelectedRows() { + int[] sel = replacementsTable.getSelectedRows(); + for (int i = 0; i < sel.length; i++) { + sel[i] = replacementsTable.getRowSorter().convertRowIndexToModel(sel[i]); + } + + return sel; + } + + private void openButtonActionPerformed(ActionEvent evt) { + open(); + } + + private void saveAsButtonActionPerformed(ActionEvent evt) { + int[] sel = getSelectedRows(); + if (sel.length == 1) { + Replacement r = replacements.get(sel[0]); + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(Configuration.lastSaveDir.get())); + String n = r.urlPattern; + if (n.contains("?")) { + n = n.substring(0, n.indexOf('?')); + } + if (n.contains("/")) { + n = n.substring(n.lastIndexOf('/')); + } + n = Helper.makeFileName(n); + fc.setSelectedFile(new File(Configuration.lastSaveDir.get(), n)); + String ext = ".swf"; + final String extension = ext; + FileFilter swfFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(extension)) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter" + extension); + } + }; + fc.setFileFilter(swfFilter); + fc.setAcceptAllFileFilterUsed(true); + JFrame f = new JFrame(); + View.setWindowIcon(f); + if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) { + File file = Helper.fixDialogFile(fc.getSelectedFile()); + try { + Files.copy(new File(r.targetFile).toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException ex) { + View.showMessageDialog(this, translate("error.save.as") + "\r\n" + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + } + } else { + GuiAbortRetryIgnoreHandler handler = new GuiAbortRetryIgnoreHandler(); + File exportDir = new File(selectExportDir()); + for (int s : sel) { + final Replacement r = replacements.get(s); + String n = r.urlPattern; + if (n.contains("?")) { + n = n.substring(0, n.indexOf('?')); + } + if (n.contains("/")) { + n = n.substring(n.lastIndexOf('/')); + } + n = Helper.makeFileName(n); + int c = 2; + String n2 = n; + while (new File(exportDir, n2).exists()) { + if (n.contains(".")) { + n2 = n.substring(0, n.lastIndexOf('.')) + c + n.substring(n.lastIndexOf('.')); + c++; + } else { + n2 = n + c + ".swf"; + c++; + } + } + + final File outfile = new File(exportDir, n2); + try { + new RetryTask(new RunnableIOEx() { + @Override + public void run() throws IOException { + Files.copy(new File(r.targetFile).toPath(), outfile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + }, handler).run(); + } catch (IOException | InterruptedException ex) { + break; + } + } + } + } + + private void replaceButtonActionPerformed(ActionEvent evt) { + int[] sel = getSelectedRows(); + if (sel.length > 0) { + Replacement r = replacements.get(sel[0]); + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); + String ext = ".swf"; + final String extension = ext; + FileFilter swfFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(extension)) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter" + extension); + } + }; + fc.setFileFilter(swfFilter); + fc.setAcceptAllFileFilterUsed(true); + JFrame f = new JFrame(); + View.setWindowIcon(f); + if (fc.showOpenDialog(f) == JFileChooser.APPROVE_OPTION) { + File file = Helper.fixDialogFile(fc.getSelectedFile()); + try { + Files.copy(file.toPath(), new File(r.targetFile).toPath(), StandardCopyOption.REPLACE_EXISTING); + tableModel.fireTableCellUpdated(sel[0], 1/*size*/); + } catch (IOException ex) { + View.showMessageDialog(f, translate("error.replace") + "\r\n" + ex.getLocalizedMessage(), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + } + } + } + + private void copyUrlButtonActionPerformed(ActionEvent evt) { + int[] sel = getSelectedRows(); + StringBuilder copyText = new StringBuilder(); + for (int sc : sel) { + Replacement r = replacements.get(sc); + if (copyText.length() > 0) { + copyText.append(System.lineSeparator()); + } + copyText.append(r.urlPattern); + } + + if (copyText.length() > 0) { + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + StringSelection stringSelection = new StringSelection(copyText.toString()); + clipboard.setContents(stringSelection, null); + } + } + + private void renameButtonActionPerformed(ActionEvent evt) { + int[] sel = getSelectedRows(); + if (sel.length > 0) { + Replacement r = replacements.get(sel[0]); + String s = View.showInputDialog("URL", r.urlPattern); + if (s != null) { + r.urlPattern = s; + tableModel.setValueAt(s, sel[0], 2/*url*/); + } + } + } + + private void clearButtonActionPerformed(ActionEvent evt) { + for (Replacement r : replacements) { + File f; + try { + f = (new File(Main.tempFile(r.targetFile))); + if (f.exists()) { + f.delete(); + } + } catch (IOException ex) { + Logger.getLogger(ProxyFrame.class.getName()).log(Level.SEVERE, null, ex); + } + } + tableModel.setRowCount(0); + replacements.clear(); + saveReplacements(); + } + + private void removeButtonActionPerformed(ActionEvent evt) { + int[] sel = getSelectedRows(); + Arrays.sort(sel); + for (int i = sel.length - 1; i >= 0; i--) { + tableModel.removeRow(sel[i]); + Replacement r = replacements.remove(sel[i]); + saveReplacements(); + File f = (new File(r.targetFile)); + if (f.exists()) { + f.delete(); + } + } + } + + private void switchStateButtonActionPerformed(ActionEvent evt) { + Main.switchProxy(); + } + + /** + * Switch proxy state + */ + public void switchState() { + started = !started; + if (started) { + int port = 0; + try { + port = Integer.parseInt(portField.getText()); + } catch (NumberFormatException nfe) { + } + if ((port <= 0) || (port > 65535)) { + View.showMessageDialog(this, translate("error.port"), translate("error"), JOptionPane.ERROR_MESSAGE); + started = false; + return; + } + List catchedContentTypes = new ArrayList<>(); + catchedContentTypes.add("application/x-shockwave-flash"); + catchedContentTypes.add("application/x-javascript"); + catchedContentTypes.add("application/javascript"); + catchedContentTypes.add("text/javascript"); + catchedContentTypes.add("application/json"); + catchedContentTypes.add("text/xml"); + catchedContentTypes.add("application/xml"); + catchedContentTypes.add("application/octet-stream"); + if (!Server.startServer(port, replacements, catchedContentTypes, this, this)) { + JOptionPane.showMessageDialog(this, translate("error.start.server").replace("%port%", "" + port), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + started = false; + return; + } + switchButton.setText(translate("proxy.stop")); + portField.setEditable(false); + } else { + Server.stopServer(); + switchButton.setText(translate("proxy.start")); + portField.setEditable(true); + } + } + + /** + * Mouse clicked event + * + * @param e event + */ + @Override + public void mouseClicked(MouseEvent e) { + if (e.getSource() == replacementsTable) { + if (e.getClickCount() == 2) { + open(); + } + } + } + + /** + * Mouse pressed event + * + * @param e event + */ + @Override + public void mousePressed(MouseEvent e) { + } + + /** + * Mouse released event + * + * @param e event + */ + @Override + public void mouseReleased(MouseEvent e) { + } + + /** + * Mouse entered event + * + * @param e event + */ + @Override + public void mouseEntered(MouseEvent e) { + } + + /** + * Mouse exited event + * + * @param e event + */ + @Override + public void mouseExited(MouseEvent e) { + } + + /** + * Method called when specified contentType is received + * + * @param contentType Content type + * @param url URL of the method + * @param data Data stream + * @return replacement data + */ + @Override + public byte[] catched(String contentType, String url, InputStream data) { + boolean swfOnly = false; + if (contentType.contains(";")) { + contentType = contentType.substring(0, contentType.indexOf(';')); + } + if ((!sniffSWFCheckBox.isSelected()) && (contentType.equals("application/x-shockwave-flash"))) { + return null; + } + if ((!sniffJSCheckBox.isSelected()) && (contentType.equals("application/javascript") || contentType.equals("application/x-javascript") || contentType.equals("text/javascript") || contentType.equals("application/json"))) { + return null; + } + if ((!sniffXMLCheckBox.isSelected()) && (contentType.equals("application/xml") || contentType.equals("text/xml"))) { + return null; + } + if ((!sniffOSCheckBox.isSelected()) && (contentType.equals("application/octet-stream"))) { + return null; + } + + byte[] result = null; + + boolean cont = false; + for (Replacement r : replacements) { + if (r.matches(url)) { + cont = true; + break; + } + } + if (!cont) { + try { + byte[] hdr = new byte[3]; + if (data.read(hdr) != 3) { + throw new IOException(); + } + + String shdr = new String(hdr); + if (swfOnly && ((!shdr.equals("FWS")) && (!shdr.equals("CWS")) && (!shdr.equals("ZWS")))) { + return null; //NOT SWF + } + + String tempFilePath = Main.tempFile(url); + data.reset(); + byte[] dataArray = Helper.readStream(data); + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(new File(tempFilePath)))) { + fos.write(dataArray); + } + + result = SWFDecompilerPlugin.fireProxyFileCatched(dataArray); + + Replacement r = new Replacement(url, tempFilePath); + r.lastAccess = Calendar.getInstance(); + replacements.add(r); + saveReplacements(); + tableModel.addRow(new Object[]{ + r.lastAccess == null ? "" : format.format(r.lastAccess.getTime()), + new SizeItem(r.targetFile), + r.urlPattern + }); + } catch (IOException e) { + } + } + + return result; + } + + /** + * Shows or hides this {@code Window} depending on the value of parameter + * {@code b}. + * + * @param b if {@code true}, makes the {@code Window} visible, otherwise + * hides the {@code Window}. If the {@code Window} and/or its owner are not + * yet displayable, both are made displayable. The {@code Window} will be + * validated prior to being made visible. If the {@code Window} is already + * visible, this will bring the {@code Window} to the front.

+ * If {@code false}, hides this {@code Window}, its subcomponents, and all + * of its owned children. The {@code Window} and its subcomponents can be + * made visible again with a call to {@code #setVisible(true)}. + * @see java.awt.Component#isDisplayable + * @see java.awt.Component#setVisible + * @see java.awt.Window#toFront + * @see java.awt.Window#dispose + */ + @Override + public void setVisible(boolean b) { + if (b == true) { + Main.addTrayIcon(); + } + super.setVisible(b); + } + + @Override + public void replaced(Replacement replacement, String url, String contentType) { + int index = replacements.indexOf(replacement); + tableModel.setValueAt(replacement.lastAccess == null ? "" : format.format(replacement.lastAccess.getTime()), index, 0); + tableModel.setValueAt(new SizeItem(replacement.targetFile), index, 1); + tableModel.setValueAt(replacement.urlPattern, index, 2); + } +}