Added AS3 Display missing namespaces along traits as §§namespace("url")

Added #1892 AS3 option to select SWF dependencies to properly resolve namespaces, types, etc. (currently in GUI only)
Fixed AS3 P-code ValueKind namespaces handling
Fixed AS3 direct editation - namespace definition without explicit value
This commit is contained in:
Jindra Petřík
2023-03-19 22:43:41 +01:00
parent f24453d525
commit dc2dc32fee
25 changed files with 533 additions and 77 deletions
@@ -47,6 +47,7 @@ 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.gfx.GfxConvertor;
import com.jpexs.decompiler.flash.gui.abc.LinkDialog;
import com.jpexs.decompiler.flash.gui.debugger.DebugListener;
import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools;
import com.jpexs.decompiler.flash.gui.pipes.FirstInstance;
@@ -119,6 +120,7 @@ import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -1500,6 +1502,7 @@ public class Main {
boolean first = true;
SWF firstSWF = null;
Openable firstOpenable = null;
List<OpenableList> openableLists = new ArrayList<>();
for (int index = 0; index < sourceInfos.length; index++) {
OpenableSourceInfo sourceInfo = sourceInfos[index];
OpenableList openables = null;
@@ -1532,6 +1535,7 @@ public class Main {
continue;
}
openableLists.add(openables);
final OpenableList openables1 = openables;
final boolean first1 = first;
first = false;
@@ -1558,6 +1562,31 @@ public class Main {
}
}
if (mainFrame != null) {
for (OpenableList openableList:openableLists) {
for (Openable openable:openableList) {
if (openable instanceof SWF) {
SWF swf = (SWF) openable;
SwfSpecificCustomConfiguration conf = Configuration.getSwfSpecificCustomConfiguration(swf.getShortPathTitle());
if (conf != null) {
String abcDependencies = conf.getCustomData(CustomConfigurationKeys.KEY_ABC_DEPENDENCIES, "");
if (!abcDependencies.isEmpty()) {
String[] parts = (abcDependencies + LinkDialog.ABC_DEPS_SEPARATOR).split(Pattern.quote(LinkDialog.ABC_DEPS_SEPARATOR));
List<String> preselectedNames = new ArrayList<>();
for (String part : parts) {
if (!part.isEmpty()) {
preselectedNames.add(part);
}
}
swf.setAbcIndexDependencies(namesToSwfs(preselectedNames));
}
}
}
}
}
}
loadingDialog.setVisible(false);
shouldCloseWhenClosingLoadingDialog = false;
@@ -2816,6 +2845,51 @@ public class Main {
}
}
public static List<SWF> namesToSwfs(List<String> names) {
List<SWF> ret = new ArrayList<>();
Map<String, SWF> swfs = new LinkedHashMap<>();
populateAllSWFs(swfs);
for (String name : names) {
if (swfs.containsKey(name)) {
ret.add(swfs.get(name));
}
}
return ret;
}
public static void populateAllSWFs(Map<String, SWF> swfs) {
if (mainFrame == null) {
return;
}
List<OpenableList> ols = mainFrame.getPanel().getSwfs();
for (OpenableList ol : ols) {
for (Openable op : ol) {
if (op instanceof SWF) {
SWF swf = (SWF) op;
populateSwf(swfs, swf, swf.getShortPathTitle()); //swf.getShortFileName());
}
}
}
}
private static void populateSwf(Map<String, SWF> ret, SWF swf, String name) {
int pos = 1;
String baseName = name;
while (ret.containsKey(name)) {
pos++;
name = baseName + "[" + pos + "]";
}
ret.put(name, swf);
for (Tag t : swf.getTags()) {
if (t instanceof DefineBinaryDataTag) {
DefineBinaryDataTag binaryData = (DefineBinaryDataTag) t;
if (binaryData.innerSwf != null) {
populateSwf(ret, binaryData.innerSwf, binaryData.innerSwf.getShortPathTitle());//name + " / " + t.getTagName() + " (" + ((DefineBinaryDataTag) t).getCharacterId() + ")");
}
}
}
}
public static void exit() {
if (mainFrame != null && mainFrame.getPanel() != null) {
mainFrame.getPanel().scrollPosStorage.saveScrollPos(mainFrame.getPanel().getCurrentTree().getCurrentTreeItem());
@@ -100,6 +100,7 @@ import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
@@ -127,7 +128,6 @@ import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
@@ -211,6 +211,8 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener<Scr
private final JButton cancelDecompiledButton = new JButton(AppStrings.translate("button.cancel"), View.getIcon("cancel16"));
private String lastDecompiled = null;
private JLabel linksLabel = new JLabel("");
public MainPanel getMainPanel() {
View.checkAccess();
@@ -260,6 +262,7 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener<Scr
setDecompiledEditMode(false);
navigator.setAbc(abc);
updateConstList();
updateLinksLabel();
}
public void updateConstList() {
@@ -998,7 +1001,10 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener<Scr
toolbarPanel = new JPanel(new BorderLayout());
toolbarPanel.add(iconsPanel, BorderLayout.WEST);
JPanel librarySelectPanel = new JPanel(new FlowLayout());
JPanel libraryAndLinkPanel = new JPanel(new FlowLayout());
LinkDialog linkDialog = new LinkDialog(mainPanel);
libraryComboBox = new JComboBox<>();
libraryComboBox.addItem("AIR (airglobal.swc)");
libraryComboBox.addItem("Flash (playerglobal.swc)");
@@ -1012,14 +1018,41 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener<Scr
}
SwfSpecificCustomConfiguration conf = Configuration.getOrCreateSwfSpecificCustomConfiguration(swf.getShortPathTitle());
conf.setCustomData(CustomConfigurationKeys.KEY_LIBRARY, "" + libraryComboBox.getSelectedIndex());
swf.resetAbcIndex();
reload();
linkDialog.load(getSwf());
linkDialog.save(getSwf(), true);
}
});
librarySelectPanel.add(new JLabel(AppStrings.translate("library")));
librarySelectPanel.add(libraryComboBox);
toolbarPanel.add(librarySelectPanel, BorderLayout.EAST);
libraryAndLinkPanel.add(new JLabel(AppStrings.translate("library")));
libraryAndLinkPanel.add(libraryComboBox);
libraryAndLinkPanel.add(linksLabel);
JButton linkButton = new JButton(View.getIcon("link16"));
linkButton.setToolTipText(AppStrings.translate("button.abc.linkedSwfs.hint"));
linkDialog.addSaveListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
reload();
}
});
linkButton.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
linkDialog.setLocationRelativeTo(linkButton);
Point loc = new Point(0, linkButton.getHeight());
SwingUtilities.convertPointToScreen(loc, linkButton);
linkDialog.setLocation(loc);
linkDialog.show(getSwf());
}
});
libraryAndLinkPanel.add(linkButton);
toolbarPanel.add(libraryAndLinkPanel, BorderLayout.EAST);
topPanel.add(toolbarPanel, BorderLayout.CENTER);
@@ -1384,7 +1417,22 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener<Scr
}
decompiledTextArea.reloadClass();
detailPanel.methodTraitPanel.methodCodePanel.clear();
detailPanel.methodTraitPanel.methodCodePanel.clear();
updateLinksLabel();
}
private void updateLinksLabel() {
SWF swf = getSwf();
if (swf == null) {
linksLabel.setText("");
} else {
int num = swf.getNumAbcIndexDependencies();
if (num == 0) {
linksLabel.setText("");
} else {
linksLabel.setText(AppStrings.translate(num == 1 ? "abc.linkedSwfs.one" : "abc.linkedSwfs.more").replace("%num%", "" + num));
}
}
}
@Override
@@ -0,0 +1,248 @@
/*
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.gui.abc;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.configuration.CustomConfigurationKeys;
import com.jpexs.decompiler.flash.configuration.SwfSpecificCustomConfiguration;
import com.jpexs.decompiler.flash.gui.Main;
import com.jpexs.decompiler.flash.gui.MainPanel;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.treeitems.Openable;
import com.jpexs.decompiler.flash.treeitems.OpenableList;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import javax.swing.border.BevelBorder;
/**
*
* @author JPEXS
*/
public class LinkDialog extends JDialog {
public static final String ABC_DEPS_SEPARATOR = "{*sep*}";
private MainPanel mainPanel;
private JList<LinkItem> linkList;
private SWF swf;
private List<ActionListener> saveListeners = new ArrayList<>();
public void addSaveListener(ActionListener listener) {
saveListeners.add(listener);
}
public void removeSaveListener(ActionListener listener) {
saveListeners.remove(listener);
}
private void fireSave() {
for (ActionListener listener : saveListeners) {
listener.actionPerformed(new ActionEvent(this, 0, "SAVE"));
}
}
public LinkDialog(MainPanel mainPanel) {
this.mainPanel = mainPanel;
setUndecorated(true);
setResizable(false);
getRootPane().setWindowDecorationStyle(JRootPane.NONE);
setSize(400, 300);
addWindowListener(new WindowAdapter() {
@Override
public void windowDeactivated(WindowEvent e) {
save(swf, false);
swf = null;
dispose();
}
});
JPanel customLibraryPanel = new JPanel(new BorderLayout());
linkList = new JList<>();
linkList.setCellRenderer(new CustomLibraryListCellRenderer());
linkList.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
int index = linkList.locationToIndex(e.getPoint());
LinkItem item = linkList.getModel().getElementAt(index);
item.setSelected(!item.isSelected());
linkList.repaint(linkList.getCellBounds(index, index));
}
}
});
customLibraryPanel.add(linkList, BorderLayout.CENTER);
customLibraryPanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
setContentPane(customLibraryPanel);
}
public void load(SWF swf) {
this.swf = swf;
SwfSpecificCustomConfiguration conf = Configuration.getSwfSpecificCustomConfiguration(swf.getShortPathTitle());
List<SWF> selectedSWFs = new ArrayList<>();
if (conf != null) {
String abcDependencies = conf.getCustomData(CustomConfigurationKeys.KEY_ABC_DEPENDENCIES, "");
if (!abcDependencies.isEmpty()) {
String[] parts = (abcDependencies + ABC_DEPS_SEPARATOR).split(Pattern.quote(ABC_DEPS_SEPARATOR));
List<String> preselectedNames = new ArrayList<>();
for (String part : parts) {
if (!part.isEmpty()) {
preselectedNames.add(part);
}
}
selectedSWFs = Main.namesToSwfs(preselectedNames);
}
}
populateSWFs(swf, selectedSWFs);
}
public void show(SWF swf) {
load(swf);
setVisible(true);
}
public void save(SWF swf, boolean force) {
Map<String, SWF> map = getSelectedSwfs();
SwfSpecificCustomConfiguration conf = Configuration.getOrCreateSwfSpecificCustomConfiguration(swf.getShortPathTitle());
String oldValue = conf.getCustomData(CustomConfigurationKeys.KEY_ABC_DEPENDENCIES, "");
String newValue = String.join(ABC_DEPS_SEPARATOR, map.keySet());
conf.setCustomData(CustomConfigurationKeys.KEY_ABC_DEPENDENCIES, newValue);
List<SWF> swfs = new ArrayList<>(map.values());
if (!Objects.equals(oldValue, newValue) || force) {
swf.setAbcIndexDependencies(swfs);
fireSave();
}
}
private Map<String, SWF> getSelectedSwfs() {
ListModel<LinkItem> model = linkList.getModel();
Map<String, SWF> ret = new LinkedHashMap<>();
for (int i = 0; i < model.getSize(); i++) {
LinkItem item = model.getElementAt(i);
if (!item.isSelected()) {
continue;
}
ret.put(item.getName(), item.getSwf());
}
return ret;
}
private void populateSWFs(SWF ignoreSWF, List<SWF> selectedSWFs) {
Map<String, SWF> swfs = new LinkedHashMap<>();
Main.populateAllSWFs(swfs);
DefaultListModel<LinkItem> listModel = new DefaultListModel<>();
for (String key : swfs.keySet()) {
SWF swf = swfs.get(key);
if (swf == ignoreSWF) {
continue;
}
boolean selected = false;
for (SWF s : selectedSWFs) {
if (s == swf) {
selected = true;
}
}
listModel.addElement(new LinkItem(key, swf, selected));
}
linkList.setModel(listModel);
pack();
}
}
class LinkItem {
private String name;
private SWF swf;
private boolean selected;
public LinkItem(String name, SWF swf, boolean selected) {
this.name = name;
this.swf = swf;
this.selected = selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
public boolean isSelected() {
return selected;
}
public String getName() {
return name;
}
public SWF getSwf() {
return swf;
}
@Override
public String toString() {
return name;
}
}
class CustomLibraryListCellRenderer extends JCheckBox implements ListCellRenderer<LinkItem> {
@Override
public Component getListCellRendererComponent(JList<? extends LinkItem> list, LinkItem value, int index, boolean isSelected, boolean cellHasFocus) {
setEnabled(list.isEnabled());
setSelected(value.isSelected());
setFont(list.getFont());
setBackground(list.getBackground());
setForeground(list.getForeground());
setText(value.toString());
return this;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1121,4 +1121,8 @@ button.morph.start = Start
button.morph.end = End
header.displayrect.unit.pixels = pixels
header.displayrect.unit.twips = twips
header.displayrect.unit.twips = twips
button.abc.linkedSwfs.hint = Other SWF dependencies
abc.linkedSwfs.one = +1 swf
abc.linkedSwfs.more = +%num% swfs