/*
* Copyright (C) 2010-2014 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package com.jpexs.decompiler.flash.gui;
import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler;
import com.jpexs.decompiler.flash.AppStrings;
import com.jpexs.decompiler.flash.ApplicationInfo;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.RenameType;
import com.jpexs.decompiler.flash.abc.ScriptPack;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.abc.types.traits.TraitClass;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.dumpview.DumpInfo;
import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode;
import com.jpexs.decompiler.flash.exporters.BinaryDataExporter;
import com.jpexs.decompiler.flash.exporters.FontExporter;
import com.jpexs.decompiler.flash.exporters.ImageExporter;
import com.jpexs.decompiler.flash.exporters.MorphShapeExporter;
import com.jpexs.decompiler.flash.exporters.MovieExporter;
import com.jpexs.decompiler.flash.exporters.ShapeExporter;
import com.jpexs.decompiler.flash.exporters.SoundExporter;
import com.jpexs.decompiler.flash.exporters.TextExporter;
import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode;
import com.jpexs.decompiler.flash.exporters.modes.FontExportMode;
import com.jpexs.decompiler.flash.exporters.modes.FramesExportMode;
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.TextExportMode;
import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.FramesExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.ImageExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.MorphShapeExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.MovieExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.ShapeExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings;
import com.jpexs.decompiler.flash.gui.abc.ABCPanel;
import com.jpexs.decompiler.flash.gui.abc.ClassesListTreeModel;
import com.jpexs.decompiler.flash.gui.abc.DeobfuscationDialog;
import com.jpexs.decompiler.flash.gui.abc.treenodes.TreeElement;
import com.jpexs.decompiler.flash.gui.action.ActionPanel;
import com.jpexs.decompiler.flash.gui.dumpview.DumpTree;
import com.jpexs.decompiler.flash.gui.dumpview.DumpTreeModel;
import com.jpexs.decompiler.flash.gui.dumpview.DumpViewPanel;
import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel;
import com.jpexs.decompiler.flash.gui.timeline.TimelineFrame;
import com.jpexs.decompiler.flash.gui.treenodes.SWFBundleNode;
import com.jpexs.decompiler.flash.gui.treenodes.SWFNode;
import com.jpexs.decompiler.flash.gui.treenodes.StringNode;
import com.jpexs.decompiler.flash.helpers.Freed;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
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.JPEGTablesTag;
import com.jpexs.decompiler.flash.tags.SymbolClassTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.tags.base.BoundedTag;
import com.jpexs.decompiler.flash.tags.base.ButtonTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.Container;
import com.jpexs.decompiler.flash.tags.base.ContainerItem;
import com.jpexs.decompiler.flash.tags.base.DrawableTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler;
import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.text.ParseException;
import com.jpexs.decompiler.flash.timeline.DepthState;
import com.jpexs.decompiler.flash.timeline.Frame;
import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.decompiler.flash.treeitems.FrameNodeItem;
import com.jpexs.decompiler.flash.treeitems.SWFList;
import com.jpexs.decompiler.flash.treeitems.StringItem;
import com.jpexs.decompiler.flash.treeitems.TreeItem;
import com.jpexs.decompiler.flash.treenodes.ContainerNode;
import com.jpexs.decompiler.flash.treenodes.FrameNode;
import com.jpexs.decompiler.flash.treenodes.HeaderNode;
import com.jpexs.decompiler.flash.treenodes.TagNode;
import com.jpexs.decompiler.flash.treenodes.TreeNode;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.decompiler.flash.xfl.FLAVersion;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Path;
import com.jpexs.helpers.SerializableImage;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JColorChooser;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.plaf.basic.BasicTreeUI;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
/**
*
* @author JPEXS
*/
public final class MainPanel extends JPanel implements ActionListener, TreeSelectionListener, SearchListener, Freed {
private final MainFrame mainFrame;
private final List swfs;
private ABCPanel abcPanel;
private ActionPanel actionPanel;
private final JPanel welcomePanel;
private final MainFrameStatusPanel statusPanel;
private final MainFrameMenu mainMenu;
private final JProgressBar progressBar = new JProgressBar(0, 100);
private DeobfuscationDialog deobfuscationDialog;
public TagTree tagTree;
public DumpTree dumpTree;
private final FlashPlayerPanel flashPanel;
private final JPanel contentPanel;
private final JPanel displayPanel;
private JPanel folderPreviewPanel;
private boolean isWelcomeScreen = true;
private static final String CARDPREVIEWPANEL = "Preview card";
private static final String CARDFOLDERPREVIEWPANEL = "Folder preview card";
private static final String CARDEMPTYPANEL = "Empty card";
private static final String CARDDUMPVIEW = "Dump view";
private static final String CARDACTIONSCRIPTPANEL = "ActionScript card";
private static final String CARDACTIONSCRIPT3PANEL = "ActionScript3 card";
private static final String CARDHEADER = "Header card";
private static final String DETAILCARDAS3NAVIGATOR = "Traits list";
private static final String DETAILCARDEMPTYPANEL = "Empty card";
private static final String SPLIT_PANE1 = "SPLITPANE1";
private static final String WELCOME_PANEL = "WELCOMEPANEL";
private final JSplitPane splitPane1;
private final JSplitPane splitPane2;
private boolean splitsInited = false;
private JPanel detailPanel;
private JTextField filterField = new MyTextField("");
private JPanel searchPanel;
private final PreviewPanel previewPanel;
private final HeaderInfoPanel headerPanel;
private DumpViewPanel dumpViewPanel;
private final JPanel treePanel;
private TreePanelMode treePanelMode;
private AbortRetryIgnoreHandler errorHandler = new GuiAbortRetryIgnoreHandler();
private CancellableWorker setSourceWorker;
public TreeNode oldNode;
private SoundTagPlayer soundThread = null;
public static final String ACTION_SELECT_BKCOLOR = "SELECTCOLOR";
public static final String ACTION_REPLACE = "REPLACE";
// play morph shape in 2 second(s)
public static final int MORPH_SHAPE_ANIMATION_LENGTH = 2;
public static final int MORPH_SHAPE_ANIMATION_FRAME_RATE = 30;
private static final Logger logger = Logger.getLogger(MainPanel.class.getName());
public void setPercent(int percent) {
progressBar.setValue(percent);
progressBar.setVisible(true);
}
public void hidePercent() {
if (progressBar.isVisible()) {
progressBar.setVisible(false);
}
}
public MainFrame getMainFrame() {
return mainFrame;
}
static {
try {
File.createTempFile("temp", ".swf").delete(); //First call to this is slow, so make it first
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
private static void addTab(JTabbedPane tabbedPane, Component tab, String title, Icon icon) {
tabbedPane.add(tab);
JLabel lbl = new JLabel(title);
lbl.setIcon(icon);
lbl.setIconTextGap(5);
lbl.setHorizontalTextPosition(SwingConstants.RIGHT);
tabbedPane.setTabComponentAt(tabbedPane.getTabCount() - 1, lbl);
}
public void setStatus(String s) {
statusPanel.setStatus(s);
}
public void setWorkStatus(String s, CancellableWorker worker) {
statusPanel.setWorkStatus(s, worker);
}
private JPanel createWelcomePanel() {
JPanel welcomePanel = new JPanel();
welcomePanel.setLayout(new BoxLayout(welcomePanel, BoxLayout.Y_AXIS));
JLabel welcomeToLabel = new JLabel(translate("startup.welcometo"));
welcomeToLabel.setFont(welcomeToLabel.getFont().deriveFont(40));
welcomeToLabel.setAlignmentX(0.5f);
JPanel appNamePanel = new JPanel(new FlowLayout());
JLabel jpLabel = new JLabel("JPEXS ");
jpLabel.setAlignmentX(0.5f);
jpLabel.setForeground(new Color(0, 0, 160));
jpLabel.setFont(new Font("Tahoma", Font.BOLD, 50));
jpLabel.setHorizontalAlignment(SwingConstants.CENTER);
appNamePanel.add(jpLabel);
JLabel ffLabel = new JLabel("Free Flash ");
ffLabel.setAlignmentX(0.5f);
ffLabel.setFont(new Font("Tahoma", Font.BOLD, 50));
ffLabel.setHorizontalAlignment(SwingConstants.CENTER);
appNamePanel.add(ffLabel);
JLabel decLabel = new JLabel("Decompiler");
decLabel.setAlignmentX(0.5f);
decLabel.setForeground(Color.red);
decLabel.setFont(new Font("Tahoma", Font.BOLD, 50));
decLabel.setHorizontalAlignment(SwingConstants.CENTER);
appNamePanel.add(decLabel);
appNamePanel.setAlignmentX(0.5f);
welcomePanel.add(Box.createGlue());
welcomePanel.add(welcomeToLabel);
welcomePanel.add(appNamePanel);
JLabel startLabel = new JLabel(translate("startup.selectopen"));
startLabel.setAlignmentX(0.5f);
startLabel.setFont(startLabel.getFont().deriveFont(30));
welcomePanel.add(startLabel);
welcomePanel.add(Box.createGlue());
return welcomePanel;
}
private JPanel createFolderPreviewCard() {
JPanel folderPreviewCard = new JPanel(new BorderLayout());
folderPreviewPanel = new JPanel(new WrapLayout(FlowLayout.LEFT));
folderPreviewCard.add(new JScrollPane(folderPreviewPanel), BorderLayout.CENTER);
return folderPreviewCard;
}
private JPanel createDumpPreviewCard() {
JPanel dumpViewCard = new JPanel(new BorderLayout());
dumpViewPanel = new DumpViewPanel();
dumpViewCard.add(new JScrollPane(dumpViewPanel), BorderLayout.CENTER);
return dumpViewCard;
}
public String translate(String key) {
return mainFrame.translate(key);
}
public MainPanel(MainFrame mainFrame, MainFrameMenu mainMenu, FlashPlayerPanel flashPanel) {
super();
this.mainFrame = mainFrame;
this.mainMenu = mainMenu;
this.flashPanel = flashPanel;
mainFrame.setTitle(ApplicationInfo.applicationVerName);
setLayout(new BorderLayout());
swfs = new ArrayList<>();
detailPanel = new JPanel();
detailPanel.setLayout(new CardLayout());
JPanel whitePanel = new JPanel();
whitePanel.setBackground(Color.white);
detailPanel.add(whitePanel, DETAILCARDEMPTYPANEL);
CardLayout cl2 = (CardLayout) (detailPanel.getLayout());
cl2.show(detailPanel, DETAILCARDEMPTYPANEL);
UIManager.getDefaults().put("TreeUI", BasicTreeUI.class.getName());
tagTree = new TagTree(null, this);
tagTree.addTreeSelectionListener(this);
DragSource dragSource = DragSource.getDefaultDragSource();
dragSource.createDefaultDragGestureRecognizer(tagTree, DnDConstants.ACTION_COPY_OR_MOVE, new DragGestureListener() {
@Override
public void dragGestureRecognized(DragGestureEvent dge) {
dge.startDrag(DragSource.DefaultCopyDrop, new Transferable() {
@Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{DataFlavor.javaFileListFlavor};
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(DataFlavor.javaFileListFlavor);
}
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if (flavor.equals(DataFlavor.javaFileListFlavor)) {
List files;
String tempDir = System.getProperty("java.io.tmpdir");
if (!tempDir.endsWith(File.separator)) {
tempDir += File.separator;
}
Random rnd = new Random();
tempDir += "ffdec" + File.separator + "export" + File.separator + System.currentTimeMillis() + "_" + rnd.nextInt(1000);
File fTempDir = new File(tempDir);
if (!fTempDir.exists()) {
if (!fTempDir.mkdirs()) {
if (!fTempDir.exists()) {
throw new IOException("cannot create directory " + fTempDir);
}
}
}
File ftemp = new File(tempDir);
ExportDialog exd = new ExportDialog(null);
files = exportSelection(errorHandler, tempDir, exd);
files.clear();
File[] fs = ftemp.listFiles();
files.addAll(Arrays.asList(fs));
Main.stopWork();
for (File f : files) {
f.deleteOnExit();
}
new File(tempDir).deleteOnExit();
return files;
}
return null;
}
}, new DragSourceListener() {
@Override
public void dragEnter(DragSourceDragEvent dsde) {
enableDrop(false);
}
@Override
public void dragOver(DragSourceDragEvent dsde) {
}
@Override
public void dropActionChanged(DragSourceDragEvent dsde) {
}
@Override
public void dragExit(DragSourceEvent dse) {
}
@Override
public void dragDropEnd(DragSourceDropEvent dsde) {
enableDrop(true);
}
});
}
});
tagTree.createContextMenu(swfs);
dumpTree = new DumpTree(null, this);
dumpTree.addTreeSelectionListener(this);
dumpTree.createContextMenu();
statusPanel = new MainFrameStatusPanel(this);
add(statusPanel, BorderLayout.SOUTH);
displayPanel = new JPanel(new CardLayout());
previewPanel = new PreviewPanel(this, flashPanel);
displayPanel.add(previewPanel, CARDPREVIEWPANEL);
displayPanel.add(createFolderPreviewCard(), CARDFOLDERPREVIEWPANEL);
displayPanel.add(createDumpPreviewCard(), CARDDUMPVIEW);
headerPanel = new HeaderInfoPanel();
displayPanel.add(headerPanel, CARDHEADER);
displayPanel.add(new JPanel(), CARDEMPTYPANEL);
showCard(CARDEMPTYPANEL);
searchPanel = new JPanel();
searchPanel.setLayout(new BorderLayout());
searchPanel.add(filterField, BorderLayout.CENTER);
searchPanel.add(new JLabel(View.getIcon("search16")), BorderLayout.WEST);
JLabel closeSearchButton = new JLabel(View.getIcon("cancel16"));
closeSearchButton.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
filterField.setText("");
doFilter();
searchPanel.setVisible(false);
}
});
searchPanel.add(closeSearchButton, BorderLayout.EAST);
treePanel = new JPanel(new BorderLayout());
treePanel.add(searchPanel, BorderLayout.SOUTH);
filterField.addActionListener(this);
searchPanel.setVisible(false);
filterField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e) {
warn();
}
@Override
public void removeUpdate(DocumentEvent e) {
warn();
}
@Override
public void insertUpdate(DocumentEvent e) {
warn();
}
public void warn() {
doFilter();
}
});
//displayPanel.setBorder(BorderFactory.createLineBorder(Color.black));
splitPane2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, treePanel, detailPanel);
splitPane1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, splitPane2, displayPanel);
welcomePanel = createWelcomePanel();
add(welcomePanel, BorderLayout.CENTER);
splitPane1.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent pce) {
if (splitsInited) {
Configuration.guiSplitPane1DividerLocation.set((int) pce.getNewValue());
}
}
});
splitPane2.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent pce) {
if (detailPanel.isVisible()) {
Configuration.guiSplitPane2DividerLocation.set((int) pce.getNewValue());
}
}
});
CardLayout cl3 = new CardLayout();
contentPanel = new JPanel(cl3);
contentPanel.add(welcomePanel, WELCOME_PANEL);
contentPanel.add(splitPane1, SPLIT_PANE1);
add(contentPanel);
cl3.show(contentPanel, WELCOME_PANEL);
tagTree.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if ((e.getKeyCode() == 'F') && (e.isControlDown())) {
searchPanel.setVisible(true);
filterField.requestFocusInWindow();
}
}
});
detailPanel.setVisible(false);
showDumpView(Configuration.dumpView.get());
updateUi();
//Opening files with drag&drop to main window
enableDrop(true);
}
public void load(SWFList newSwfs, boolean first) {
previewPanel.clear();
swfs.add(newSwfs);
for (SWF swf : newSwfs) {
tagTree.setModel(new TagTreeModel(mainFrame, swfs));
dumpTree.setModel(new DumpTreeModel(swfs));
if (swf.isAS3) {
if (abcPanel == null) {
abcPanel = new ABCPanel(this);
displayPanel.add(abcPanel, CARDACTIONSCRIPT3PANEL);
detailPanel.add(abcPanel.tabbedPane, DETAILCARDAS3NAVIGATOR);
}
abcPanel.setSwf(swf);
} else {
if (actionPanel == null) {
actionPanel = new ActionPanel(this);
displayPanel.add(actionPanel, CARDACTIONSCRIPTPANEL);
}
}
expandSwfNodes();
for (Tag t : swf.tags) {
if (t instanceof JPEGTablesTag) {
swf.jtt = (JPEGTablesTag) t;
}
}
if (Configuration.autoRenameIdentifiers.get()) {
try {
swf.deobfuscateIdentifiers(RenameType.TYPENUMBER);
swf.assignClassesToSymbols();
clearCache();
if (abcPanel != null) {
abcPanel.reload();
}
updateClassesList();
} catch (InterruptedException ex) {
Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex);
}
}
showDetail(DETAILCARDEMPTYPANEL);
showCard(CARDEMPTYPANEL);
updateUi(swf);
/*if (first && Configuration.gotoMainClassOnStartup.get()) {
gotoDocumentClass(swf);
}*/
first = false;
}
}
private void updateUi(final SWF swf) {
mainFrame.setTitle(ApplicationInfo.applicationVerName + (Configuration.displayFileName.get() ? " - " + swf.getFileTitle() : ""));
List abcList = swf.abcList;
boolean hasAbc = !abcList.isEmpty();
if (hasAbc) {
abcPanel.setSwf(swf);
}
if (isWelcomeScreen) {
CardLayout cl = (CardLayout) (contentPanel.getLayout());
cl.show(contentPanel, SPLIT_PANE1);
isWelcomeScreen = false;
}
mainMenu.updateComponents(swf, abcList);
}
private void updateUi() {
if (!isWelcomeScreen && swfs.isEmpty()) {
CardLayout cl = (CardLayout) (contentPanel.getLayout());
cl.show(contentPanel, WELCOME_PANEL);
isWelcomeScreen = true;
}
mainFrame.setTitle(ApplicationInfo.applicationVerName);
mainMenu.updateComponents(null, null);
}
public void closeAll() {
swfs.clear();
oldNode = null;
previewPanel.clear();
if (abcPanel != null) {
abcPanel.clearSwf();
}
if (actionPanel != null) {
actionPanel.clearSource();
}
updateUi();
refreshTree();
}
public void close(SWFList swfList) {
swfs.remove(swfList);
if (abcPanel != null) {
for (SWF swf : swfList) {
if (abcPanel.swf == swf) {
abcPanel.clearSwf();
}
}
}
if (actionPanel != null) {
actionPanel.clearSource();
}
oldNode = null;
previewPanel.clear();
updateUi();
refreshTree();
}
public void enableDrop(boolean value) {
if (value) {
setDropTarget(new DropTarget() {
@Override
public synchronized void drop(DropTargetDropEvent dtde) {
try {
dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
@SuppressWarnings("unchecked")
List droppedFiles = (List) dtde.getTransferable().getTransferData(DataFlavor.javaFileListFlavor);
if (!droppedFiles.isEmpty()) {
String path = droppedFiles.get(0).getAbsolutePath();
Main.openFile(path, null);
}
} catch (UnsupportedFlavorException | IOException ex) {
}
}
});
} else {
setDropTarget(null);
}
}
public void updateClassesList() {
List nodes = getASTreeNodes(tagTree);
boolean updateNeeded = false;
for (TreeNode n : nodes) {
if (n.getItem() instanceof ClassesListTreeModel) {
((ClassesListTreeModel) n.getItem()).update();
updateNeeded = true;
}
}
refreshTree();
if (updateNeeded) {
View.execInEventDispatch(new Runnable() {
@Override
public void run() {
tagTree.updateUI();
}
});
}
}
public void doFilter() {
List nodes = getASTreeNodes(tagTree);
boolean updateNeeded = false;
for (TreeNode n : nodes) {
if (n.getItem() instanceof ClassesListTreeModel) {
((ClassesListTreeModel) n.getItem()).setFilter(filterField.getText());
updateNeeded = true;
}
}
if (updateNeeded) {
View.execInEventDispatch(new Runnable() {
@Override
public void run() {
tagTree.updateUI();
}
});
}
}
@Override
public void setVisible(boolean b) {
super.setVisible(b);
if (b) {
if (abcPanel != null) {
abcPanel.initSplits();
}
if (actionPanel != null) {
actionPanel.initSplits();
}
final MainPanel t = this;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
splitPane1.setDividerLocation(Configuration.guiSplitPane1DividerLocation.get(getWidth() / 3));
int confDivLoc = Configuration.guiSplitPane2DividerLocation.get(splitPane2.getHeight() * 3 / 5);
if (confDivLoc > splitPane2.getHeight() - 10) { //In older releases, divider location was saved when detailPanel was invisible too
confDivLoc = splitPane2.getHeight() * 3 / 5;
}
splitPane2.setDividerLocation(confDivLoc);
previewPanel.setDividerLocation(Configuration.guiPreviewSplitPaneDividerLocation.get(previewPanel.getWidth() / 2));
splitPos = splitPane2.getDividerLocation();
splitsInited = true;
previewPanel.setSplitsInited();
}
});
}
}
public static void getShapes(List list, List shapes) {
for (ContainerItem t : list) {
if (t instanceof Container) {
getShapes(((Container) t).getSubItems(), shapes);
}
if ((t instanceof DefineShapeTag)
|| (t instanceof DefineShape2Tag)
|| (t instanceof DefineShape3Tag)
|| (t instanceof DefineShape4Tag)) {
shapes.add((Tag) t);
}
}
}
public static void getFonts(List list, List fonts) {
for (ContainerItem t : list) {
if (t instanceof Container) {
getFonts(((Container) t).getSubItems(), fonts);
}
if ((t instanceof DefineFontTag)
|| (t instanceof DefineFont2Tag)
|| (t instanceof DefineFont3Tag)
|| (t instanceof DefineFont4Tag)) {
fonts.add((Tag) t);
}
}
}
public static void getMorphShapes(List list, List morphShapes) {
for (ContainerItem t : list) {
if (t instanceof Container) {
getMorphShapes(((Container) t).getSubItems(), morphShapes);
}
if ((t instanceof DefineMorphShapeTag) || (t instanceof DefineMorphShape2Tag)) {
morphShapes.add((Tag) t);
}
}
}
public static void getImages(List list, List images) {
for (ContainerItem t : list) {
if (t instanceof Container) {
getImages(((Container) t).getSubItems(), images);
}
if ((t instanceof DefineBitsTag)
|| (t instanceof DefineBitsJPEG2Tag)
|| (t instanceof DefineBitsJPEG3Tag)
|| (t instanceof DefineBitsJPEG4Tag)
|| (t instanceof DefineBitsLosslessTag)
|| (t instanceof DefineBitsLossless2Tag)) {
images.add((Tag) t);
}
}
}
public static void getTexts(List list, List texts) {
for (ContainerItem t : list) {
if (t instanceof Container) {
getTexts(((Container) t).getSubItems(), texts);
}
if ((t instanceof DefineTextTag)
|| (t instanceof DefineText2Tag)
|| (t instanceof DefineEditTextTag)) {
texts.add((Tag) t);
}
}
}
public static void getSprites(List list, List sprites) {
for (ContainerItem t : list) {
if (t instanceof Container) {
getSprites(((Container) t).getSubItems(), sprites);
}
if (t instanceof DefineSpriteTag) {
sprites.add((Tag) t);
}
}
}
public static void getButtons(List list, List buttons) {
for (ContainerItem t : list) {
if (t instanceof Container) {
getButtons(((Container) t).getSubItems(), buttons);
}
if ((t instanceof DefineButtonTag) || (t instanceof DefineButton2Tag)) {
buttons.add((Tag) t);
}
}
}
public void renameIdentifier(SWF swf, String identifier) throws InterruptedException {
String oldName = identifier;
String newName = View.showInputDialog(translate("rename.enternew"), oldName);
if (newName != null) {
if (!oldName.equals(newName)) {
swf.renameAS2Identifier(oldName, newName);
View.showMessageDialog(null, translate("rename.finished.identifier"));
updateClassesList();
reload(true);
}
}
}
public void renameMultiname(List abcList, int multiNameIndex) {
String oldName = "";
if (abcPanel.abc.constants.getMultiname(multiNameIndex).name_index > 0) {
oldName = abcPanel.abc.constants.getString(abcPanel.abc.constants.getMultiname(multiNameIndex).name_index);
}
String newName = View.showInputDialog(translate("rename.enternew"), oldName);
if (newName != null) {
if (!oldName.equals(newName)) {
int mulCount = 0;
for (ABCContainerTag cnt : abcList) {
ABC abc = cnt.getABC();
for (int m = 1; m < abc.constants.getMultinameCount(); m++) {
int ni = abc.constants.getMultiname(m).name_index;
String n = "";
if (ni > 0) {
n = abc.constants.getString(ni);
}
if (n.equals(oldName)) {
abc.renameMultiname(m, newName);
mulCount++;
}
}
}
View.showMessageDialog(null, translate("rename.finished.multiname").replace("%count%", "" + mulCount));
if (abcPanel != null) {
abcPanel.reload();
}
updateClassesList();
reload(true);
abcPanel.hilightScript(abcPanel.swf, abcPanel.decompiledTextArea.getScriptLeaf().getPath().toString());
}
}
}
public List getASTreeNodes(TagTree tree) {
List result = new ArrayList<>();
TagTreeModel tm = (TagTreeModel) tree.getModel();
if (tm == null) {
return result;
}
TreeNode root = tm.getRoot();
for (int i = 0; i < tm.getChildCount(root); i++) {
// first level node can be SWFNode and SWFBundleNode
TreeNode node = tm.getChild(root, i);
if (node instanceof SWFBundleNode) {
for (int j = 0; j < tm.getChildCount(node); j++) {
// child of SWFBundleNode should be SWFNode
SWFNode swfNode = (SWFNode) tm.getChild(node, j);
result.add(swfNode.scriptsNode);
}
} else if (node instanceof SWFNode) {
SWFNode swfNode = (SWFNode) tm.getChild(root, i);
result.add(swfNode.scriptsNode);
}
}
return result;
}
public boolean confirmExperimental() {
return View.showConfirmDialog(null, translate("message.confirm.experimental"), translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION;
}
private SearchDialog searchDialog;
public List exportSelection(AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException {
List ret = new ArrayList<>();
List sel = tagTree.getAllSelected(tagTree);
List allSwfs = new ArrayList<>();
for (SWFList swfList : swfs) {
for (SWF swf : swfList) {
allSwfs.add(swf);
}
}
for (int j = 0; j < allSwfs.size(); j++) {
List as3scripts = new ArrayList<>();
List images = new ArrayList<>();
List shapes = new ArrayList<>();
List morphshapes = new ArrayList<>();
List movies = new ArrayList<>();
List sounds = new ArrayList<>();
List texts = new ArrayList<>();
List as12scripts = new ArrayList<>();
List binaryData = new ArrayList<>();
Map> frames = new HashMap<>();
List fonts = new ArrayList<>();
SWF swf = allSwfs.get(j);
for (TreeNode d : sel) {
SWF selectedNodeSwf = d.getItem().getSwf();
if (!allSwfs.contains(selectedNodeSwf)) {
allSwfs.add(selectedNodeSwf);
}
if (selectedNodeSwf != swf) {
continue;
}
if (d instanceof ContainerNode) {
ContainerNode n = (ContainerNode) d;
TreeNodeType nodeType = TagTree.getTreeNodeType(n.getItem());
if (nodeType == TreeNodeType.IMAGE) {
images.add((Tag) n.getItem());
}
if (nodeType == TreeNodeType.SHAPE) {
shapes.add((Tag) n.getItem());
}
if (nodeType == TreeNodeType.MORPH_SHAPE) {
morphshapes.add((Tag) n.getItem());
}
if (nodeType == TreeNodeType.AS) {
as12scripts.add(n);
}
if (nodeType == TreeNodeType.MOVIE) {
movies.add((Tag) n.getItem());
}
if (nodeType == TreeNodeType.SOUND) {
sounds.add((Tag) n.getItem());
}
if (nodeType == TreeNodeType.BINARY_DATA) {
binaryData.add((Tag) n.getItem());
}
if (nodeType == TreeNodeType.TEXT) {
texts.add((Tag) n.getItem());
}
if (nodeType == TreeNodeType.FONT) {
fonts.add((Tag) n.getItem());
}
}
if (d instanceof FrameNode) {
FrameNode fn = (FrameNode) d;
if (!fn.scriptsNode) {
FrameNodeItem fni = (FrameNodeItem) d.getItem();
Timelined parent = fni.getParent();
int frame = fni.getFrame() - 1; //Fix to zero based
int parentId = 0;
if (parent instanceof CharacterTag) {
parentId = ((CharacterTag) parent).getCharacterId();
}
if (!frames.containsKey(parentId)) {
frames.put(parentId, new ArrayList());
}
frames.get(parentId).add(frame);
}
}
if (d instanceof TreeElement) {
if (((TreeElement) d).isLeaf()) {
TreeElement treeElement = (TreeElement) d;
as3scripts.add((ScriptPack) treeElement.getItem());
}
}
}
if (selFile == null) {
selFile = selectExportDir();
if (selFile == null) {
return new ArrayList<>();
}
}
final ScriptExportMode scriptMode = export.getValue(ScriptExportMode.class);
ret.addAll(new ImageExporter().exportImages(handler, selFile + File.separator + "images", images,
new ImageExportSettings(export.getValue(ImageExportMode.class))));
ret.addAll(new ShapeExporter().exportShapes(handler, selFile + File.separator + "shapes", shapes,
new ShapeExportSettings(export.getValue(ShapeExportMode.class))));
ret.addAll(new MorphShapeExporter().exportMorphShapes(handler, selFile + File.separator + "morphshapes", morphshapes,
new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class))));
ret.addAll(new TextExporter().exportTexts(handler, selFile + File.separator + "texts", texts,
new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get())));
ret.addAll(new MovieExporter().exportMovies(handler, selFile + File.separator + "movies", movies,
new MovieExportSettings(export.getValue(MovieExportMode.class))));
ret.addAll(new SoundExporter().exportSounds(handler, selFile + File.separator + "sounds", sounds,
new SoundExportSettings(export.getValue(SoundExportMode.class))));
ret.addAll(new BinaryDataExporter().exportBinaryData(handler, selFile + File.separator + "binaryData", binaryData,
new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class))));
ret.addAll(new FontExporter().exportFonts(handler, selFile + File.separator + "fonts", fonts,
new FontExportSettings(export.getValue(FontExportMode.class))));
for (Entry> entry : frames.entrySet()) {
ret.addAll(swf.exportFrames(handler, selFile + File.separator + "frames", entry.getKey(), entry.getValue(),
new FramesExportSettings(export.getValue(FramesExportMode.class))));
}
List abcList = swf.abcList;
if (abcPanel != null) {
for (int i = 0; i < as3scripts.size(); i++) {
ScriptPack tls = as3scripts.get(i);
Main.startWork(translate("work.exporting") + " " + (i + 1) + "/" + as3scripts.size() + " " + tls.getPath() + " ...");
ret.add(tls.export(selFile, abcList, scriptMode, Configuration.parallelSpeedUp.get()));
}
} else {
List allNodes = new ArrayList<>();
List allAs12Scripts = new ArrayList<>();
if (abcPanel == null) {
allAs12Scripts = getASTreeNodes(tagTree);
}
for (TreeNode asn : allAs12Scripts) {
allNodes.add(asn);
TagNode.setExport(allNodes, false);
TagNode.setExport(as12scripts, true);
ret.addAll(TagNode.exportNodeAS(handler, allNodes, selFile, scriptMode, null));
}
}
}
return ret;
}
public SWFList getCurrentSwfList() {
SWF swf = getCurrentSwf();
if (swf == null) {
return null;
}
return swf.swfList;
}
public SWF getCurrentSwf() {
if (swfs == null || swfs.isEmpty()) {
return null;
}
if (treePanelMode == TreePanelMode.TAG_TREE) {
TreeNode treeNode = (TreeNode) tagTree.getLastSelectedPathComponent();
if (treeNode == null) {
return swfs.get(0).get(0);
}
if (treeNode instanceof SWFBundleNode) {
return null;
}
return treeNode.getItem().getSwf();
} else if (treePanelMode == TreePanelMode.DUMP_TREE) {
DumpInfo dumpInfo = (DumpInfo) dumpTree.getLastSelectedPathComponent();
if (dumpInfo == null) {
return null;
}
return DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf();
}
return null;
}
private void clearCache() {
if (abcPanel != null) {
abcPanel.decompiledTextArea.clearScriptCache();
}
if (actionPanel != null) {
actionPanel.clearCache();
}
}
public void gotoDocumentClass(SWF swf) {
if (swf == null) {
return;
}
String documentClass = null;
loopdc:
for (Tag t : swf.tags) {
if (t instanceof SymbolClassTag) {
SymbolClassTag sc = (SymbolClassTag) t;
for (int i = 0; i < sc.tags.length; i++) {
if (sc.tags[i] == 0) {
documentClass = sc.names[i];
break loopdc;
}
}
}
}
if (documentClass != null && !Configuration.dumpView.get()) {
showDetail(DETAILCARDAS3NAVIGATOR);
showCard(CARDACTIONSCRIPT3PANEL);
abcPanel.setSwf(swf);
abcPanel.hilightScript(swf, documentClass);
}
}
public void disableDecompilationChanged() {
clearCache();
if (abcPanel != null) {
abcPanel.reload();
}
reload(true);
updateClassesList();
}
public void searchAs() {
if (searchDialog == null) {
searchDialog = new SearchDialog(getMainFrame().getWindow());
}
searchDialog.setVisible(true);
if (searchDialog.result) {
final String txt = searchDialog.searchField.getText();
if (!txt.isEmpty()) {
final SWF swf = getCurrentSwf();
new CancellableWorker() {
@Override
protected Void doInBackground() throws Exception {
boolean found = false;
if (searchDialog.searchInASRadioButton.isSelected()) {
if (swf.isAS3) {
if (abcPanel != null && abcPanel.search(txt, searchDialog.ignoreCaseCheckBox.isSelected(), searchDialog.regexpCheckBox.isSelected())) {
found = true;
View.execInEventDispatch(new Runnable() {
@Override
public void run() {
showDetail(DETAILCARDAS3NAVIGATOR);
showCard(CARDACTIONSCRIPT3PANEL);
}
});
}
} else {
if (actionPanel.search(txt, searchDialog.ignoreCaseCheckBox.isSelected(), searchDialog.regexpCheckBox.isSelected())) {
found = true;
View.execInEventDispatch(new Runnable() {
@Override
public void run() {
showCard(CARDACTIONSCRIPTPANEL);
}
});
}
}
} else if (searchDialog.searchInTextsRadioButton.isSelected()) {
if (searchText(txt, searchDialog.ignoreCaseCheckBox.isSelected(), searchDialog.regexpCheckBox.isSelected(), swf)) {
found = true;
}
}
if (!found) {
View.showMessageDialog(null, translate("message.search.notfound").replace("%searchtext%", txt), translate("message.search.notfound.title"), JOptionPane.INFORMATION_MESSAGE);
}
return null;
}
}.execute();
}
}
}
private boolean searchText(String txt, boolean ignoreCase, boolean regexp, SWF swf) {
if ((txt != null) && (!txt.isEmpty())) {
SearchPanel textSearchPanel = previewPanel.getTextPanel().getSearchPanel();
textSearchPanel.setOptions(ignoreCase, regexp);
List found = new ArrayList<>();
Pattern pat = null;
if (regexp) {
pat = Pattern.compile(txt, ignoreCase ? Pattern.CASE_INSENSITIVE : 0);
} else {
pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? Pattern.CASE_INSENSITIVE : 0);
}
for (Tag tag : swf.tags) {
if (tag instanceof TextTag) {
TextTag textTag = (TextTag) tag;
if (pat.matcher(textTag.getFormattedText()).find()) {
found.add(textTag);
}
}
}
textSearchPanel.setSearchText(txt);
return textSearchPanel.setResults(found);
}
return false;
}
@Override
public void updateSearchPos(TextTag item) {
setTagTreeSelectedNode(item);
previewPanel.getTextPanel().updateSearchPos();
}
private void setDumpTreeSelectedNode(DumpInfo dumpInfo) {
DumpTreeModel dtm = (DumpTreeModel) dumpTree.getModel();
TreePath tp = dtm.getDumpInfoPath(dumpInfo);
if (tp != null) {
dumpTree.setSelectionPath(tp);
dumpTree.scrollPathToVisible(tp);
} else {
showCard(CARDEMPTYPANEL);
}
}
public void setTagTreeSelectedNode(TreeItem treeItem) {
TagTreeModel ttm = (TagTreeModel) tagTree.getModel();
TreePath tp = ttm.getTagPath(treeItem);
if (tp != null) {
tagTree.setSelectionPath(tp);
tagTree.scrollPathToVisible(tp);
} else {
showCard(CARDEMPTYPANEL);
}
}
public void autoDeobfuscateChanged() {
clearCache();
if (abcPanel != null) {
abcPanel.reload();
}
reload(true);
updateClassesList();
}
public void renameOneIdentifier(final SWF swf) {
if (swf == null) {
return;
}
if (swf.fileAttributes != null && swf.fileAttributes.actionScript3) {
final int multiName = abcPanel.decompiledTextArea.getMultinameUnderCursor();
final List abcList = swf.abcList;
if (multiName > 0) {
new CancellableWorker() {
@Override
public Void doInBackground() throws Exception {
Main.startWork(translate("work.renaming") + "...");
renameMultiname(abcList, multiName);
return null;
}
@Override
protected void done() {
Main.stopWork();
}
}.execute();
} else {
View.showMessageDialog(null, translate("message.rename.notfound.multiname"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE);
}
} else {
final String identifier = actionPanel.getStringUnderCursor();
if (identifier != null) {
new CancellableWorker() {
@Override
public Void doInBackground() throws Exception {
Main.startWork(translate("work.renaming") + "...");
try {
renameIdentifier(swf, identifier);
} catch (InterruptedException ex) {
Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
@Override
protected void done() {
Main.stopWork();
}
}.execute();
} else {
View.showMessageDialog(null, translate("message.rename.notfound.identifier"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE);
}
}
}
public void exportFla(final SWF swf) {
if (swf == null) {
return;
}
JFileChooser fc = new JFileChooser();
String selDir = Configuration.lastOpenDir.get();
fc.setCurrentDirectory(new File(selDir));
if (!selDir.endsWith(File.separator)) {
selDir += File.separator;
}
String fileName = new File(swf.file).getName();
fileName = fileName.substring(0, fileName.length() - 4) + ".fla";
fc.setSelectedFile(new File(selDir + fileName));
List flaFilters = new ArrayList<>();
List xflFilters = new ArrayList<>();
List versions = new ArrayList<>();
FileFilter defaultFilter = null;
for (int i = FLAVersion.values().length - 1; i >= 0; i--) {
final FLAVersion v = FLAVersion.values()[i];
if (!swf.isAS3 && v.minASVersion() > 2) {
//This version does not support AS1/2
} else {
versions.add(v);
FileFilter f = new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || (f.getName().toLowerCase().endsWith(".fla"));
}
@Override
public String getDescription() {
return translate("filter.fla").replace("%version%", v.applicationName());
}
};
if (v == FLAVersion.CS6) {
defaultFilter = f;
fc.setFileFilter(f);
} else {
fc.addChoosableFileFilter(f);
}
flaFilters.add(f);
f = new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || (f.getName().toLowerCase().endsWith(".xfl"));
}
@Override
public String getDescription() {
return translate("filter.xfl").replace("%version%", v.applicationName());
}
};
fc.addChoosableFileFilter(f);
xflFilters.add(f);
}
}
if (defaultFilter == null) {
defaultFilter = flaFilters.get(0);
}
fc.setAcceptAllFileFilterUsed(false);
JFrame f = new JFrame();
View.setWindowIcon(f);
if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) {
Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath());
File sf = Helper.fixDialogFile(fc.getSelectedFile());
Main.startWork(translate("work.exporting.fla") + "...");
final boolean compressed = flaFilters.contains(fc.getFileFilter());
if (!compressed) {
if (sf.getName().endsWith(".fla")) {
sf = new File(sf.getAbsolutePath().substring(0, sf.getAbsolutePath().length() - 4) + ".xfl");
}
}
final FLAVersion selectedVersion = versions.get(compressed ? flaFilters.indexOf(fc.getFileFilter()) : xflFilters.indexOf(fc.getFileFilter()));
final File selfile = sf;
new CancellableWorker() {
@Override
protected Void doInBackground() throws Exception {
Helper.freeMem();
try {
if (compressed) {
swf.exportFla(errorHandler, selfile.getAbsolutePath(), new File(swf.file).getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), selectedVersion);
} else {
swf.exportXfl(errorHandler, selfile.getAbsolutePath(), new File(swf.file).getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), selectedVersion);
}
} catch (IOException ex) {
View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage(), translate("error"), JOptionPane.ERROR_MESSAGE);
}
Helper.freeMem();
return null;
}
@Override
protected void done() {
Main.stopWork();
if (Configuration.openFolderAfterFlaExport.get()) {
try {
Desktop.getDesktop().open(selfile.getAbsoluteFile().getParentFile());
} catch (IOException ex) {
Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}.execute();
}
}
private Map splitTextRecords(String texts) {
String[] textsArr = texts.split(Helper.newLine + Configuration.textExportSingleFileSeparator.get() + Helper.newLine);
String recordSeparator = Helper.newLine + Configuration.textExportSingleFileRecordSeparator.get() + Helper.newLine;
Map result = new HashMap<>();
for (String text : textsArr) {
String[] textArr = text.split(Helper.newLine, 2);
String idLine = textArr[0];
if (idLine.startsWith("ID:")) {
int id = Integer.parseInt(idLine.substring(3).trim());
String[] records = textArr[1].split(recordSeparator);
result.put(id, records);
} else {
if (View.showConfirmDialog(this, translate("error.text.import"), translate("error"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) {
return null;
}
}
}
return result;
}
private void importTextsSingleFile(File textsFile, SWF swf) {
String texts = Helper.readTextFile(textsFile.getPath());
Map records = splitTextRecords(texts);
if (records != null) {
for (int characterId : records.keySet()) {
for (Tag tag : swf.tags) {
if (tag instanceof TextTag) {
TextTag textTag = (TextTag) tag;
if (textTag.getCharacterId() == characterId) {
String[] currentRecords = records.get(characterId);
String text = textTag.getFormattedText();
if (!saveText(textTag, text, currentRecords)) {
if (View.showConfirmDialog(this, translate("error.text.import"), translate("error"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) {
return;
}
}
break;
}
}
}
}
}
}
private void importTextsSingleFileFormatted(File textsFile, SWF swf) {
String texts = Helper.readTextFile(textsFile.getPath());
Map records = splitTextRecords(texts);
if (records != null) {
for (int characterId : records.keySet()) {
for (Tag tag : swf.tags) {
if (tag instanceof TextTag) {
TextTag textTag = (TextTag) tag;
if (textTag.getCharacterId() == characterId) {
String[] currentRecords = records.get(characterId);
if (!saveText(textTag, currentRecords[0], null)) {
if (View.showConfirmDialog(this, translate("error.text.import"), translate("error"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) {
return;
}
}
break;
}
}
}
}
}
}
private void importTextsMultipleFiles(String folder, SWF swf) {
File textsFolder = new File(Path.combine(folder, TextExporter.TEXT_EXPORT_FOLDER));
String[] files = textsFolder.list(new FilenameFilter() {
private final Pattern pat = Pattern.compile("\\d+\\.txt", Pattern.CASE_INSENSITIVE);
@Override
public boolean accept(File dir, String name) {
return pat.matcher(name).matches();
}
});
for (String fileName : files) {
String texts = Helper.readTextFile(Path.combine(textsFolder.getPath(), fileName));
int characterId = Integer.parseInt(fileName.split("\\.")[0]);
String recordSeparator = Helper.newLine + Configuration.textExportSingleFileRecordSeparator.get() + Helper.newLine;
boolean formatted = !texts.contains(recordSeparator) && texts.startsWith("[" + Helper.newLine);
if (!formatted) {
String[] records = texts.split(recordSeparator);
for (Tag tag : swf.tags) {
if (tag instanceof TextTag) {
TextTag textTag = (TextTag) tag;
if (textTag.getCharacterId() == characterId) {
String text = textTag.getFormattedText();
if (!saveText(textTag, text, records)) {
if (View.showConfirmDialog(this, translate("error.text.import"), translate("error"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) {
return;
}
}
break;
}
}
}
} else {
for (Tag tag : swf.tags) {
if (tag instanceof TextTag) {
TextTag textTag = (TextTag) tag;
if (textTag.getCharacterId() == characterId) {
if (!saveText(textTag, texts, null)) {
if (View.showConfirmDialog(this, translate("error.text.import"), translate("error"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) {
return;
}
}
break;
}
}
}
}
}
}
public void importText(final SWF swf) {
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get()));
chooser.setDialogTitle(translate("import.select.directory"));
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
chooser.setAcceptAllFileFilterUsed(false);
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath();
File textsFile = new File(Path.combine(selFile, TextExporter.TEXT_EXPORT_FOLDER, TextExporter.TEXT_EXPORT_FILENAME_FORMATTED));
// try to import formatted texts
if (textsFile.exists()) {
importTextsSingleFileFormatted(textsFile, swf);
} else {
textsFile = new File(Path.combine(selFile, TextExporter.TEXT_EXPORT_FOLDER, TextExporter.TEXT_EXPORT_FILENAME_PLAIN));
// try to import plain texts
if (textsFile.exists()) {
importTextsSingleFile(textsFile, swf);
} else {
importTextsMultipleFiles(selFile, swf);
}
}
SWF.clearImageCache();
reload(true);
}
}
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) {
Main.startWork(translate("work.exporting") + "...");
final String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath();
Configuration.lastExportDir.set(Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath());
return selFile;
}
return null;
}
public void export(final boolean onlySel) {
final SWF swf = getCurrentSwf();
List