Added Set AS1/2 linkage dialog (uses ExportAssets tag) in the context menu for characters

This commit is contained in:
Jindra Petřík
2024-08-05 11:17:25 +02:00
parent ea9802ddeb
commit 6e0f90f266
11 changed files with 919 additions and 127 deletions
@@ -0,0 +1,392 @@
/*
* Copyright (C) 2010-2024 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;
import com.jpexs.decompiler.flash.gui.abc.*;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
import com.jpexs.decompiler.flash.tags.DefineFont4Tag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.ExportAssetsTag;
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.timeline.Timelined;
import java.awt.Component;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.util.HashMap;
import java.util.Map;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
/**
*
* @author JPEXS
*/
public class AsLinkageDialog extends AppDialog {
private final JButton proceedButton = new JButton(translate("button.proceed"));
private final JButton cancelButton = new JButton(translate("button.cancel"));
private final JTextField identifierTextField = new JTextField(30);
private final JTextField classNameTextField = new JTextField(30);
private final JTextField parentClassNameTextField = new JTextField(30);
private String selectedIdentifier = null;
private String selectedClass = null;
private String selectedParentClass = null;
private ExportAssetsTag originalExportAssetsTag;
private ExportAssetsTag selectedExportAssetsTag;
private String originalClassName;
private Tag selectedPosition;
private Timelined selectedTimelined;
private int result = ERROR_OPTION;
private final SWF swf;
private final int characterId;
private int characterFrame = -1;
private int exportAssetsCount = 0;
private final JLabel errorLabel = new JLabel();
private final JLabel parentClassNameLabel = new JLabel(translate("class.parentname"));
private final JRadioButton existingExportAssetsTagRadioButton = new JRadioButton(translate("linkage.notfound.exportAssets.where.existing"));
private final JRadioButton newExportAssetsTagRadioButton = new JRadioButton(translate("linkage.notfound.exportAssets.where.new"));
private static final Map<Class<?>, String> tagTypeToParentClass = new HashMap<>();
static {
tagTypeToParentClass.put(SoundTag.class, "flash.media.Sound");
tagTypeToParentClass.put(ImageTag.class, "flash.display.Bitmap");
tagTypeToParentClass.put(FontTag.class, "flash.text.Font");
tagTypeToParentClass.put(DefineFont4Tag.class, "flash.text.Font");
tagTypeToParentClass.put(DefineBinaryDataTag.class, "flash.utils.ByteArray");
tagTypeToParentClass.put(DefineSpriteTag.class, "flash.display.Sprite");
}
public static String getParentClassFromCharacter(CharacterTag ch) {
for (Class<?> cls : tagTypeToParentClass.keySet()) {
if (cls.isAssignableFrom(ch.getClass())) {
return tagTypeToParentClass.get(cls);
}
}
return null;
}
public AsLinkageDialog(Window owner, SWF swf, int characterId) {
super(owner);
this.swf = swf;
this.characterId = characterId;
CharacterTag ch = swf.getCharacter(characterId);
if (ch == null) {
throw new RuntimeException("Character " + characterId + " not found");
}
//parentClassNameTextField.setText(getParentClassFromCharacter(ch));
int frame = 1;
for (Tag t : swf.getTags()) {
if (t == ch) {
characterFrame = frame;
}
if (t instanceof ShowFrameTag) {
frame++;
}
}
frame = 1;
for (Tag t : swf.getTags()) {
if (frame >= characterFrame) {
if (t instanceof ExportAssetsTag) {
ExportAssetsTag ea = (ExportAssetsTag) t;
if (ea.tags.contains(characterId)) {
originalExportAssetsTag = ea;
}
exportAssetsCount++;
}
}
if (t instanceof ShowFrameTag) {
frame++;
}
}
String originalIdentifier = ch.getExportName();
identifierTextField.setText(originalIdentifier);
originalClassName = ch.getAs2ClassName();
if (originalClassName != null) {
classNameTextField.setText(originalClassName);
classNameTextField.setEnabled(false);
identifierTextField.setEnabled(false);
}
setDefaultCloseOperation(HIDE_ON_CLOSE);
setTitle(translate("dialog.title"));
Container cnt = getContentPane();
cnt.setLayout(new BoxLayout(cnt, BoxLayout.Y_AXIS));
cnt.add(new JLabel(translate("identifier")));
cnt.add(identifierTextField);
cnt.add(new JLabel(translate("classname")));
cnt.add(classNameTextField);
if (originalClassName == null) {
cnt.add(parentClassNameLabel);
cnt.add(parentClassNameTextField);
}
cnt.add(errorLabel);
if (originalClassName == null && originalExportAssetsTag == null) {
ButtonGroup whereToStoreMappingButtonGroup = new ButtonGroup();
whereToStoreMappingButtonGroup.add(existingExportAssetsTagRadioButton);
whereToStoreMappingButtonGroup.add(newExportAssetsTagRadioButton);
cnt.add(new JLabel(translate("linkage.notfound.exportAssets.where")));
JPanel whereToStoreMappingPanel = new JPanel(new FlowLayout());
whereToStoreMappingPanel.add(existingExportAssetsTagRadioButton);
whereToStoreMappingPanel.add(newExportAssetsTagRadioButton);
cnt.add(whereToStoreMappingPanel);
existingExportAssetsTagRadioButton.setSelected(true);
}
JPanel buttonsPanel = new JPanel(new FlowLayout());
proceedButton.addActionListener(this::okButtonActionPerformed);
cancelButton.addActionListener(this::cancelButtonActionPerformed);
buttonsPanel.add(proceedButton);
buttonsPanel.add(cancelButton);
cnt.add(buttonsPanel);
setCentralAlignment((JComponent) cnt);
if (exportAssetsCount == 0) {
newExportAssetsTagRadioButton.setSelected(true);
newExportAssetsTagRadioButton.setEnabled(false);
existingExportAssetsTagRadioButton.setEnabled(false);
}
existingExportAssetsTagRadioButton.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
checkEnabled();
}
});
newExportAssetsTagRadioButton.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
checkEnabled();
}
});
classNameTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
checkEnabled();
}
@Override
public void removeUpdate(DocumentEvent e) {
checkEnabled();
}
@Override
public void changedUpdate(DocumentEvent e) {
checkEnabled();
}
});
classNameTextField.addActionListener(this::okButtonActionPerformed);
checkEnabled();
pack();
setModal(true);
setResizable(false);
View.setWindowIcon(this);
View.centerScreen(this);
}
private void setCentralAlignment(JComponent container) {
Component[] components = container.getComponents();
for (Component component : components) {
if (component instanceof JComponent) {
((JComponent) component).setAlignmentX(Component.CENTER_ALIGNMENT);
}
}
}
private void checkEnabled() {
boolean ok = true;
String newIdentifier = identifierTextField.getText();
String newClassName = classNameTextField.getText();
if (originalClassName != null) {
ok = false;
proceedButton.setText(translate("button.ok"));
errorLabel.setText("");
}
if (!classNameTextField.isEnabled() && newIdentifier.isEmpty()) {
ok = false;
proceedButton.setText(translate("button.ok"));
errorLabel.setText(translate("error.cannotRemoveIdentifierClassExists"));
}
if (newClassName.endsWith(".")) {
ok = false;
proceedButton.setText(translate("button.ok"));
errorLabel.setText("");
}
if (newIdentifier.isEmpty() && !newClassName.isEmpty()) {
ok = false;
proceedButton.setText(translate("button.ok"));
errorLabel.setText("");
}
if (ok) {
CharacterTag ch = swf.getCharacter(characterId);
String oldClassName = ch.getAs2ClassName();
if (oldClassName == null && newClassName != null && ch.getSwf().getCharacterByExportName("__Packages." + newClassName) != null) {
ok = false;
errorLabel.setText(translate("error.alreadyExistsClass"));
}
}
if (ok) {
if (originalExportAssetsTag != null) {
proceedButton.setText(translate("button.ok"));
} else if (existingExportAssetsTagRadioButton.isSelected() && exportAssetsCount == 1) {
proceedButton.setText(translate("button.ok"));
} else {
proceedButton.setText(translate("button.proceed"));
}
} else {
proceedButton.setText(translate("button.ok"));
}
proceedButton.setEnabled(ok);
}
private void okButtonActionPerformed(ActionEvent evt) {
if (!proceedButton.isEnabled()) {
return;
}
setVisible(false);
if (originalExportAssetsTag != null) {
selectedExportAssetsTag = originalExportAssetsTag;
} else {
if (existingExportAssetsTagRadioButton.isSelected()) {
SelectTagOfTypeDialog selectExportAssetsDialog = new SelectTagOfTypeDialog(owner, swf, ExportAssetsTag.class, "ExportAssets", characterFrame);
selectedExportAssetsTag = (ExportAssetsTag) selectExportAssetsDialog.showDialog();
if (selectedExportAssetsTag == null) {
cancelButtonActionPerformed(evt);
return;
}
}
if (newExportAssetsTagRadioButton.isSelected()) {
SelectTagPositionDialog selectTagPositionDialog = new SelectTagPositionDialog(owner, swf, false, "ExportAssets", characterFrame);
if (selectTagPositionDialog.showDialog() != OK_OPTION) {
cancelButtonActionPerformed(evt);
return;
}
selectedPosition = selectTagPositionDialog.getSelectedTag();
selectedTimelined = selectTagPositionDialog.getSelectedTimelined();
}
}
result = OK_OPTION;
selectedIdentifier = identifierTextField.getText();
selectedClass = classNameTextField.getText();
selectedParentClass = parentClassNameTextField.getText();
setVisible(false);
}
private void cancelButtonActionPerformed(ActionEvent evt) {
selectedIdentifier = null;
selectedClass = null;
selectedParentClass = null;
selectedPosition = null;
selectedTimelined = null;
selectedExportAssetsTag = null;
characterFrame = -1;
result = CANCEL_OPTION;
setVisible(false);
}
public int showDialog() {
selectedIdentifier = null;
selectedClass = null;
selectedParentClass = null;
selectedPosition = null;
selectedTimelined = null;
selectedExportAssetsTag = null;
setVisible(true);
return result;
}
public Tag getSelectedPosition() {
return selectedPosition;
}
public String getSelectedClass() {
return selectedClass;
}
public String getSelectedParentClass() {
return selectedParentClass;
}
public ExportAssetsTag getSelectedExportAssetsTag() {
return selectedExportAssetsTag;
}
public Timelined getSelectedTimelined() {
return selectedTimelined;
}
public int getCharacterFrame() {
return characterFrame;
}
public String getSelectedIdentifier() {
return selectedIdentifier;
}
}
@@ -73,6 +73,7 @@ public class As3ClassLinkageDialog extends AppDialog {
private String selectedParentClass = null;
private ABCContainerTag selectedAbcContainer;
private SymbolClassTag selectedSymbolClassTag;
private SymbolClassTag originalSymbolClassTag;
private Tag selectedPosition;
private Timelined selectedTimelined;
private int result = ERROR_OPTION;
@@ -165,6 +166,10 @@ public class As3ClassLinkageDialog extends AppDialog {
abcContainers.add((ABCContainerTag) t);
}
if (t instanceof SymbolClassTag) {
SymbolClassTag sc = (SymbolClassTag) t;
if (sc.tags.contains(characterId)) {
originalSymbolClassTag = sc;
}
symbolClassCount++;
}
}
@@ -240,12 +245,15 @@ public class As3ClassLinkageDialog extends AppDialog {
ButtonGroup whereToStoreMappingButtonGroup = new ButtonGroup();
whereToStoreMappingButtonGroup.add(existingSymbolClassTagRadioButton);
whereToStoreMappingButtonGroup.add(newSymbolClassTagRadioButton);
doNotCreateNewClassPanel.add(new JLabel(translate("class.notfound.onlySetClassName.symbolClass.where")));
if (originalSymbolClassTag == null) {
doNotCreateNewClassPanel.add(new JLabel(translate("class.notfound.onlySetClassName.symbolClass.where")));
JPanel whereToStoreMappingPanel = new JPanel(new FlowLayout());
whereToStoreMappingPanel.add(existingSymbolClassTagRadioButton);
whereToStoreMappingPanel.add(newSymbolClassTagRadioButton);
doNotCreateNewClassPanel.add(whereToStoreMappingPanel);
JPanel whereToStoreMappingPanel = new JPanel(new FlowLayout());
whereToStoreMappingPanel.add(existingSymbolClassTagRadioButton);
whereToStoreMappingPanel.add(newSymbolClassTagRadioButton);
doNotCreateNewClassPanel.add(whereToStoreMappingPanel);
}
existingSymbolClassTagRadioButton.setSelected(true);
setCentralAlignment(doNotCreateNewClassPanel);
@@ -381,10 +389,10 @@ public class As3ClassLinkageDialog extends AppDialog {
String newClassName = classNameTextField.getText();
if (newClassName.isEmpty()) {
/*if (newClassName.isEmpty()) {
ok = false;
errorLabel.setText("");
}
}*/
if (newClassName.endsWith(".")) {
ok = false;
@@ -400,7 +408,7 @@ public class As3ClassLinkageDialog extends AppDialog {
} else if (oldClassNames.size() == 1 && newClassName.equals(oldClassNames.iterator().next())) {
ok = false;
errorLabel.setText(translate("error.needToModify"));
} else if (swf.getCharacterByClass(newClassName) != null) {
} else if (!newClassName.isEmpty() && swf.getCharacterByClass(newClassName) != null) {
ok = false;
errorLabel.setText(translate("error.alreadyAssignedClass"));
}
@@ -409,42 +417,48 @@ public class As3ClassLinkageDialog extends AppDialog {
CardLayout cl = (CardLayout) classFoundOrNotOrErrorPanel.getLayout();
if (ok) {
List<String> classNames = new ArrayList<>();
classNames.add(newClassName);
foundInAbcContainer = null;
try {
List<ScriptPack> scriptPacks = swf.getScriptPacksByClassNames(classNames);
if (!scriptPacks.isEmpty()) {
ABC foundInAbc = scriptPacks.get(0).abc; //Assume there is only single pack with the class
if (newClassName.isEmpty()) {
proceedButton.setText(translate("button.ok"));
cl.show(classFoundOrNotOrErrorPanel, ERROR_CARD);
errorLabel.setText("");
} else {
List<String> classNames = new ArrayList<>();
classNames.add(newClassName);
foundInAbcContainer = null;
try {
List<ScriptPack> scriptPacks = swf.getScriptPacksByClassNames(classNames);
if (!scriptPacks.isEmpty()) {
ABC foundInAbc = scriptPacks.get(0).abc; //Assume there is only single pack with the class
boolean foundContainer = false;
for (ABCContainerTag cnt : abcContainers) {
if (cnt.getABC() == foundInAbc) {
foundInAbcContainer = cnt;
break;
boolean foundContainer = false;
for (ABCContainerTag cnt : abcContainers) {
if (cnt.getABC() == foundInAbc) {
foundInAbcContainer = cnt;
break;
}
}
}
} catch (Exception ex) {
//ignore
}
} catch (Exception ex) {
//ignore
}
if (foundInAbcContainer != null) {
cl.show(classFoundOrNotOrErrorPanel, CLASS_FOUND_CARD);
} else {
cl.show(classFoundOrNotOrErrorPanel, CLASS_NOT_FOUND_CARD);
if (createClassRadioButton.isSelected()) {
if (existingAbcTagRadioButton.isSelected() && abcCount == 1) {
proceedButton.setText(translate("button.ok"));
} else {
proceedButton.setText(translate("button.proceed"));
}
if (foundInAbcContainer != null) {
cl.show(classFoundOrNotOrErrorPanel, CLASS_FOUND_CARD);
} else {
if (existingSymbolClassTagRadioButton.isSelected() && symbolClassCount == 1) {
proceedButton.setText(translate("button.ok"));
cl.show(classFoundOrNotOrErrorPanel, CLASS_NOT_FOUND_CARD);
if (createClassRadioButton.isSelected()) {
if (existingAbcTagRadioButton.isSelected() && abcCount == 1) {
proceedButton.setText(translate("button.ok"));
} else {
proceedButton.setText(translate("button.proceed"));
}
} else {
proceedButton.setText(translate("button.proceed"));
if (originalSymbolClassTag != null || (existingSymbolClassTagRadioButton.isSelected() && symbolClassCount == 1)) {
proceedButton.setText(translate("button.ok"));
} else {
proceedButton.setText(translate("button.proceed"));
}
}
}
}
@@ -461,46 +475,55 @@ public class As3ClassLinkageDialog extends AppDialog {
return;
}
setVisible(false);
if (foundInAbcContainer == null) {
if (createClassRadioButton.isSelected()) {
if (existingAbcTagRadioButton.isSelected()) {
SelectTagOfTypeDialog selectDoABCDialog = new SelectTagOfTypeDialog(owner, swf, ABCContainerTag.class, "DoABC", characterFrame);
selectedAbcContainer = (ABCContainerTag) selectDoABCDialog.showDialog();
if (selectedAbcContainer == null) {
cancelButtonActionPerformed(evt);
return;
boolean emptyClassName = classNameTextField.getText().isEmpty();
if (emptyClassName) {
selectedSymbolClassTag = originalSymbolClassTag;
} else {
if (foundInAbcContainer == null) {
if (createClassRadioButton.isSelected()) {
if (existingAbcTagRadioButton.isSelected()) {
SelectTagOfTypeDialog selectDoABCDialog = new SelectTagOfTypeDialog(owner, swf, ABCContainerTag.class, "DoABC", characterFrame);
selectedAbcContainer = (ABCContainerTag) selectDoABCDialog.showDialog();
if (selectedAbcContainer == null) {
cancelButtonActionPerformed(evt);
return;
}
}
}
if (newAbcTagRadioButton.isSelected()) {
SelectTagPositionDialog selectTagPositionDialog = new SelectTagPositionDialog(owner, swf, false, "DoABC", characterFrame);
if (selectTagPositionDialog.showDialog() != OK_OPTION) {
cancelButtonActionPerformed(evt);
return;
if (newAbcTagRadioButton.isSelected()) {
SelectTagPositionDialog selectTagPositionDialog = new SelectTagPositionDialog(owner, swf, false, "DoABC", characterFrame);
if (selectTagPositionDialog.showDialog() != OK_OPTION) {
cancelButtonActionPerformed(evt);
return;
}
selectedPosition = selectTagPositionDialog.getSelectedTag();
selectedTimelined = selectTagPositionDialog.getSelectedTimelined();
}
} else {
if (originalSymbolClassTag != null) {
selectedSymbolClassTag = originalSymbolClassTag;
} else {
if (existingSymbolClassTagRadioButton.isSelected()) {
SelectTagOfTypeDialog selectSymbolClassDialog = new SelectTagOfTypeDialog(owner, swf, SymbolClassTag.class, "SymbolClass", characterFrame);
selectedSymbolClassTag = (SymbolClassTag) selectSymbolClassDialog.showDialog();
if (selectedSymbolClassTag == null) {
cancelButtonActionPerformed(evt);
return;
}
}
if (newSymbolClassTagRadioButton.isSelected()) {
SelectTagPositionDialog selectTagPositionDialog = new SelectTagPositionDialog(owner, swf, false, "SymbolClass", characterFrame);
if (selectTagPositionDialog.showDialog() != OK_OPTION) {
cancelButtonActionPerformed(evt);
return;
}
selectedPosition = selectTagPositionDialog.getSelectedTag();
selectedTimelined = selectTagPositionDialog.getSelectedTimelined();
}
}
selectedPosition = selectTagPositionDialog.getSelectedTag();
selectedTimelined = selectTagPositionDialog.getSelectedTimelined();
}
} else {
if (existingSymbolClassTagRadioButton.isSelected()) {
SelectTagOfTypeDialog selectSymbolClassDialog = new SelectTagOfTypeDialog(owner, swf, SymbolClassTag.class, "SymbolClass", characterFrame);
selectedSymbolClassTag = (SymbolClassTag) selectSymbolClassDialog.showDialog();
if (selectedSymbolClassTag == null) {
cancelButtonActionPerformed(evt);
return;
}
}
if (newSymbolClassTagRadioButton.isSelected()) {
SelectTagPositionDialog selectTagPositionDialog = new SelectTagPositionDialog(owner, swf, false, "SymbolClass", characterFrame);
if (selectTagPositionDialog.showDialog() != OK_OPTION) {
cancelButtonActionPerformed(evt);
return;
}
selectedPosition = selectTagPositionDialog.getSelectedTag();
selectedTimelined = selectTagPositionDialog.getSelectedTimelined();
}
selectedAbcContainer = foundInAbcContainer;
}
} else {
selectedAbcContainer = foundInAbcContainer;
}
if (selectedAbcContainer != null) {
@@ -516,7 +539,7 @@ public class As3ClassLinkageDialog extends AppDialog {
}
}
createClass = foundInAbcContainer == null && createClassRadioButton.isSelected();
createClass = !emptyClassName && foundInAbcContainer == null && createClassRadioButton.isSelected();
result = OK_OPTION;
selectedClass = classNameTextField.getText();
@@ -0,0 +1,30 @@
# Copyright (C) 2024 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/>.
dialog.title = AS linkage
button.ok = OK
button.proceed = Proceed
button.cancel = Cancel
identifier = ActionScript identifier:
classname = ActionScript2 class (fully qualified):
class.parentname = Parent class name (fully qualified):
error.alreadyExistsClass = Error: This class is already exists
error.cannotRemoveIdentifierClassExists = Error: Cannot remove identifier, class already exists
linkage.notfound.exportAssets.where = Where to store the linkage data:
linkage.notfound.exportAssets.where.existing = Existing ExportAssets tag
linkage.notfound.exportAssets.where.new = New ExportAssets tag
@@ -0,0 +1,30 @@
# Copyright (C) 2024 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/>.
dialog.title = AS vazba
button.ok = OK
button.proceed = Pokra\u010dovat
button.cancel = Storno
identifier = ActionScript identifik\u00e1tor:
classname = ActionScript2 t\u0159\u00edda (pln\u011b kvalifikovan\u00e1):
class.parentname = N\u00e1zev rodi\u010dovsk\u00e9 t\u0159\u00eddy (pln\u011b kvalifikovan\u00fd):
error.alreadyExistsClass = Chyba: Tato t\u0159\u00edda ji\u017e existuje
error.cannotRemoveIdentifierClassExists = Chyba: Nelze odstranit idenitifik\u00e1tor, t\u0159\u00edda ji\u017e existuje
linkage.notfound.exportAssets.where = Kam um\u00edstit data o vazb\u011b:
linkage.notfound.exportAssets.where.existing = Existuj\u00edc\u00ed ExportAssets tag
linkage.notfound.exportAssets.where.new = Nov\u00fd ExportAssets tag
@@ -1271,4 +1271,6 @@ warning.cleanAbc = This action will remove items from ABC which have zero usages
tagInfo.idType = Type of the id
contextmenu.configurePathResolving = Configure path resolving...
contextmenu.configurePathResolving = Configure path resolving...
contextmenu.setAsLinkage = Set AS linkage
@@ -1246,4 +1246,6 @@ warning.cleanAbc = Tato akce odstran\u00ed z ABC polo\u017eky, kter\u00e9 maj\u0
tagInfo.idType = Typ id
contextmenu.configurePathResolving = Nastavit resolvov\u00e1n\u00ed cest...
contextmenu.configurePathResolving = Nastavit resolvov\u00e1n\u00ed cest...
contextmenu.setAsLinkage = Nastavit AS vazbu
@@ -38,6 +38,6 @@ class.notfound.create.abc.where.existing = Existing DoABC tag
class.notfound.create.abc.where.new = New DoABC tag
class.notfound.onlySetClassName = No, just assign class name
class.notfound.onlySetClassName.symbolClass.where = Where to store the mapping:
class.notfound.onlySetClassName.symbolClass.where = Where to store the linkage data:
class.notfound.onlySetClassName.symbolClass.where.existing = Existing SymbolClass tag
class.notfound.onlySetClassName.symbolClass.where.new = New SymbolClass tag
@@ -38,6 +38,6 @@ class.notfound.create.abc.where.existing = Existuj\u00edc\u00ed DoABC tag
class.notfound.create.abc.where.new = Nov\u00fd DoABC tag
class.notfound.onlySetClassName = Ne, jen p\u0159i\u0159adit n\u00e1zev t\u0159\u00eddy
class.notfound.onlySetClassName.symbolClass.where = Kam um\u00edstit mapov\u00e1n\u00ed:
class.notfound.onlySetClassName.symbolClass.where = Kam um\u00edstit data o vazb\u011b:
class.notfound.onlySetClassName.symbolClass.where.existing = Existuj\u00edc\u00ed SymbolClass tag
class.notfound.onlySetClassName.symbolClass.where.new = Nov\u00fd SymbolClass tag
@@ -36,6 +36,7 @@ import com.jpexs.decompiler.flash.configuration.CustomConfigurationKeys;
import com.jpexs.decompiler.flash.configuration.SwfSpecificCustomConfiguration;
import com.jpexs.decompiler.flash.gui.AppDialog;
import com.jpexs.decompiler.flash.gui.AppStrings;
import com.jpexs.decompiler.flash.gui.AsLinkageDialog;
import com.jpexs.decompiler.flash.gui.ClipboardType;
import com.jpexs.decompiler.flash.gui.CollectDepthAsSpritesDialog;
import com.jpexs.decompiler.flash.gui.Main;
@@ -174,6 +175,8 @@ public class TagTreeContextMenu extends JPopupMenu {
private JMenuItem configurePathResolvingMenuItem;
private JMenuItem setAsLinkageMenuItem;
private JMenuItem setAs3ClassLinkageMenuItem;
private JMenuItem expandRecursiveMenuItem;
@@ -451,7 +454,12 @@ public class TagTreeContextMenu extends JPopupMenu {
replaceRefsWithTagMenuItem.setIcon(View.getIcon("replacewithtag16"));
add(replaceRefsWithTagMenuItem);
setAs3ClassLinkageMenuItem = new JMenuItem(mainPanel.translate("contextmenu.setAs3ClassLinkage")); //FIXME
setAsLinkageMenuItem = new JMenuItem(mainPanel.translate("contextmenu.setAsLinkage"));
setAsLinkageMenuItem.addActionListener(this::setAsLinkageActionPerformed);
setAsLinkageMenuItem.setIcon(View.getIcon("asclass16"));
add(setAsLinkageMenuItem);
setAs3ClassLinkageMenuItem = new JMenuItem(mainPanel.translate("contextmenu.setAs3ClassLinkage"));
setAs3ClassLinkageMenuItem.addActionListener(this::setAs3ClassLinkageActionPerformed);
setAs3ClassLinkageMenuItem.setIcon(View.getIcon("asclass16"));
add(setAs3ClassLinkageMenuItem);
@@ -1126,6 +1134,7 @@ public class TagTreeContextMenu extends JPopupMenu {
boolean hasExportableNodes = tree.hasExportableNodes();
setAsLinkageMenuItem.setVisible(false);
setAs3ClassLinkageMenuItem.setVisible(false);
expandRecursiveMenuItem.setVisible(false);
collapseRecursiveMenuItem.setVisible(false);
@@ -1372,6 +1381,12 @@ public class TagTreeContextMenu extends JPopupMenu {
if (cht.getSwf().isAS3() && As3ClassLinkageDialog.getParentClassFromCharacter(cht) != null) {
setAs3ClassLinkageMenuItem.setVisible(true);
}
if (!cht.getSwf().isAS3()) {
String ename = cht.getExportName();
if (ename == null || !ename.startsWith("__Packages.")) {
setAsLinkageMenuItem.setVisible(true);
}
}
}
if (tree.getModel().getChildCount(firstItem) > 0) {
@@ -2546,13 +2561,175 @@ public class TagTreeContextMenu extends JPopupMenu {
mainPanel.setTagTreeSelectedNode(mainPanel.getCurrentTree(), ((SWF) itemj.getOpenable()).getCharacter(hasCharacterId.getCharacterId()));
}
/*
How setting AS linkage should work:
User selects linkage identifier (the one really stored in ExportAssets of the character)
If the file is AS1/2:
If exists DoInitAction tag for that character, that has Object.registerClass("oldLinkageIdentifier", cls)
readonly class name is displayed
readonly identifier is displayed
exit
else
(optional) User selects desired AS2 classname and parent class name to be created
If the linkage did not exist before (no ExportAssets had such id) and linkage identifier is not empty,
user can select whether to use existing ExportAssets tag (if some exists) or to create new
On pressing OK:
If the linkage did not exist before (no ExportAssets had such id) and the new identifier is not empty,
user selects existing ExportAssets tag via dialog or selects position for new
if the dialog is cancelled, exit
If the file is AS1/2:
If the user choosed to create new class (cls is its fully qualified name)
Frame of Exportassets is determined
New DoInitAction for the character is created in the EA frame which has Object.registerClass("linkageIdentifier", cls) in code
New empty DefineSprite is created in frame 1, and its name in ExportAssets tag is set to "__Packages." + cls
New DoInitAction for the DefineSprite in frame 1 is created and it's filled with new cls Class code
The Exportassets tag is modified with the new linkage identifier
*/
private void setAsLinkageActionPerformed(ActionEvent evt) {
CharacterTag ch = (CharacterTag) getCurrentItem();
SWF swf = ch.getSwf();
AsLinkageDialog d = new AsLinkageDialog(Main.getDefaultDialogsOwner(), swf, ch.getCharacterId());
if (d.showDialog() != AppDialog.OK_OPTION) {
return;
}
String identifier = d.getSelectedIdentifier();
String className = d.getSelectedClass();
String classParent = d.getSelectedParentClass();
ExportAssetsTag ea = d.getSelectedExportAssetsTag();
if (ea == null) {
ea = new ExportAssetsTag(swf);
ea.setTimelined(swf);
if (d.getSelectedPosition() == null) {
swf.addTag(ea);
} else {
swf.addTag(swf.indexOfTag(d.getSelectedPosition()), ea);
}
}
if (!className.isEmpty()) {
int frame = 1;
int eaFrame = -1;
int regInsertPos = -1;
ReadOnlyTagList tags = swf.getTags();
for (int i = 0; i < tags.size(); i++) {
Tag t = tags.get(i);
if (t == ea) {
eaFrame = frame;
}
if (t instanceof ShowFrameTag) {
if (frame == eaFrame) {
regInsertPos = i;
break;
}
frame++;
}
}
DoInitActionTag regDoInit = new DoInitActionTag(swf);
regDoInit.spriteId = ch.getCharacterId();
regDoInit.setTimelined(swf);
ActionScript2Parser regParser = new ActionScript2Parser(swf, regDoInit);
String[] parts = className.contains(".") ? className.split("\\.") : new String[]{className};
DottedChain classDottedChain = new DottedChain(parts);
try {
List<Action> regActions = regParser.actionsFromString("Object.registerClass(\"" + Helper.escapePCodeString(identifier) + "\"," + classDottedChain.toPrintableString(false) + ");", swf.getCharset());
regDoInit.setActions(regActions);
} catch (ActionParseException | IOException | CompilationException | InterruptedException ex) {
//ignore
}
swf.addTag(regInsertPos, regDoInit);
int insertPos = 0;
tags = swf.getTags();
for (int i = 0; i < tags.size(); i++) {
Tag t = tags.get(i);
if (t instanceof ShowFrameTag) {
insertPos = i;
break;
}
}
int classCharacterId = swf.getNextCharacterId();
DefineSpriteTag classSprite = new DefineSpriteTag(swf);
classSprite.spriteId = classCharacterId;
classSprite.hasEndTag = true;
classSprite.setTimelined(swf);
String exportName = "__Packages." + className;
ExportAssetsTag classExportAssets = new ExportAssetsTag(swf);
classExportAssets.names = new ArrayList<>();
classExportAssets.names.add(exportName);
classExportAssets.tags = new ArrayList<>();
classExportAssets.tags.add(classCharacterId);
classExportAssets.setTimelined(swf);
DoInitActionTag classDoInit = new DoInitActionTag(swf);
classDoInit.spriteId = classCharacterId;
classDoInit.setTimelined(swf);
ActionScript2Parser parser = new ActionScript2Parser(swf, classDoInit);
String[] partsParent = classParent.contains(".") ? classParent.split("\\.") : new String[]{classParent};
DottedChain dcParent = new DottedChain(partsParent);
try {
List<Action> actions = parser.actionsFromString("class " + classDottedChain.toPrintableString(false) + (classParent.isEmpty() ? "" : " extends " + dcParent.toPrintableString(false)) + "{}", swf.getCharset());
classDoInit.setActions(actions);
} catch (ActionParseException | IOException | CompilationException | InterruptedException ex) {
//ignore
}
classSprite.setExportName(exportName);
swf.addTag(insertPos, classSprite);
swf.addTag(insertPos + 1, classExportAssets);
swf.addTag(insertPos + 2, classDoInit);
}
boolean found = false;
for (int i = ea.names.size() - 1; i >= 0; i--) {
if (ea.tags.get(i) == ch.getCharacterId()) {
if (identifier.isEmpty()) {
ea.tags.remove(i);
ea.names.remove(i);
} else {
ea.names.set(i, identifier);
found = true;
}
}
}
if (!identifier.isEmpty() && !found) {
ea.tags.add(ch.getCharacterId());
ea.names.add(identifier);
}
ea.setModified(true);
if (ea.names.isEmpty()) {
swf.removeTag(ea);
}
swf.clearAllCache();
swf.assignExportNamesToSymbols();
swf.setModified(true);
mainPanel.refreshTree(swf);
}
/*
How set class to character mapping work in AS3:
a) a character is selected
b) if the character already has assigned more than 1 class then exit with a message that user must do this manually
c) new class name is entered, must be different than previous
d) if new class name is already assigned to different character, exit
e) find the ABC, where class with that name is defined
e) if the classname is not empty find the ABC, where class with that name is defined
if found
determine its frame
find nearest frame >= ABC frame, such that the character is defined in it (or is defined earlier in the file)
@@ -2569,14 +2746,15 @@ public class TagTreeContextMenu extends JPopupMenu {
otherwise create new SymbolClass in that frame
or suggest not creating class, set only its name
choose whether to use existing Symbolclass or add new
AFTER PRESSING OK:
select existing SymbolClass or add new SymbolClass (on selected position).
(Selected frame for target SymbolClass must have defined the character in it or earlier in the file)
if the character did not have assigned name before (= no SymbolClass used)
choose whether to use existing Symbolclass or add new
AFTER PRESSING OK:
select existing SymbolClass or add new SymbolClass (on selected position).
(Selected frame for target SymbolClass must have defined the character in it or earlier in the file)
AFTER PRESSING OK:
I. if there is previously assigned classname, find associated SymbolClass and remove the mapping from it
II. if the previous Symbolclass is empty, remove it
III. add new character mapping to new SymbolClass determined
II. if the previous Symbolclass is empty and is not target SymbolClass, remove it
III. if the new classname is not empty, add new character mapping to new SymbolClass determined
*/
private void setAs3ClassLinkageActionPerformed(ActionEvent evt) {
CharacterTag ch = (CharacterTag) getCurrentItem();
@@ -2592,59 +2770,73 @@ public class TagTreeContextMenu extends JPopupMenu {
SymbolClassTag selectedSymbolClass = d.getSelectedSymbolClassTag();
ABCContainerTag selectedAbcContainer = d.getSelectedAbcContainer();
String className = d.getSelectedClass();
String parentClassName = d.getSelectedParentClass();
if (!d.isClassFound() && !d.doCreateClass() && selectedSymbolClass == null) { //we selected position of new SymbolClass
selectedSymbolClass = new SymbolClassTag(swf);
selectedSymbolClass.setTimelined(swf);
Tag pos = d.getSelectedPosition();
if (pos == null) {
swf.addTag(selectedSymbolClass);
} else {
swf.addTag(swf.indexOfTag(pos), selectedSymbolClass);
if (className.isEmpty() && !ch.getClassNames().isEmpty()) {
SymbolClassTag sct = d.getSelectedSymbolClassTag();
for (int i = sct.tags.size() - 1; i >= 0; i--) {
if (sct.tags.get(i) == ch.getCharacterId()) {
sct.names.remove(i);
sct.tags.remove(i);
}
}
}
if (sct.names.isEmpty()) {
swf.removeTag(sct);
}
} else {
String parentClassName = d.getSelectedParentClass();
if (!d.isClassFound() && d.doCreateClass()) {
if (d.getSelectedAbcContainer() == null) {
selectedAbcContainer = new DoABC2Tag(swf);
((Tag) selectedAbcContainer).setTimelined(swf);
if (!d.isClassFound() && !d.doCreateClass() && selectedSymbolClass == null) { //we selected position of new SymbolClass
selectedSymbolClass = new SymbolClassTag(swf);
selectedSymbolClass.setTimelined(swf);
Tag pos = d.getSelectedPosition();
if (pos == null) {
swf.addTag((Tag) selectedAbcContainer);
swf.addTag(selectedSymbolClass);
} else {
swf.addTag(swf.indexOfTag(pos), (Tag) selectedAbcContainer);
swf.addTag(swf.indexOfTag(pos), selectedSymbolClass);
}
}
String pkg = className.contains(".") ? className.substring(0, className.lastIndexOf(".")) : "";
String classSimpleName = className.contains(".") ? className.substring(className.lastIndexOf(".") + 1) : className;
String fileName = className.replace(".", "/");
String[] pkgParts = new String[0];
if (!pkg.isEmpty()) {
if (pkg.contains(".")) {
pkgParts = pkg.split("\\.");
} else {
pkgParts = new String[]{pkg};
if (!d.isClassFound() && d.doCreateClass()) {
if (d.getSelectedAbcContainer() == null) {
selectedAbcContainer = new DoABC2Tag(swf);
((Tag) selectedAbcContainer).setTimelined(swf);
Tag pos = d.getSelectedPosition();
if (pos == null) {
swf.addTag((Tag) selectedAbcContainer);
} else {
swf.addTag(swf.indexOfTag(pos), (Tag) selectedAbcContainer);
}
}
}
try {
AbcIndexing abcIndex = swf.getAbcIndex();
abcIndex.selectAbc(selectedAbcContainer.getABC());
ActionScript3Parser parser = new ActionScript3Parser(abcIndex);
DottedChain dc = new DottedChain(pkgParts);
String script = "package " + dc.toPrintableString(true) + " {"
+ (parentClassName.isEmpty() ? "" : "import " + parentClassName + ";")
+ "public class " + IdentifiersDeobfuscation.printIdentifier(true, classSimpleName) + (parentClassName.isEmpty() ? "" : " extends " + parentClassName) + " {"
+ " }"
+ "}";
parser.addScript(script, fileName, 0, 0, swf.getDocumentClass());
} catch (IOException | InterruptedException | AVM2ParseException | CompilationException ex) {
Logger.getLogger(TagTreeContextMenu.class.getName()).log(Level.SEVERE, "Error during script compilation", ex);
}
String pkg = className.contains(".") ? className.substring(0, className.lastIndexOf(".")) : "";
String classSimpleName = className.contains(".") ? className.substring(className.lastIndexOf(".") + 1) : className;
String fileName = className.replace(".", "/");
String[] pkgParts = new String[0];
if (!pkg.isEmpty()) {
if (pkg.contains(".")) {
pkgParts = pkg.split("\\.");
} else {
pkgParts = new String[]{pkg};
}
}
try {
AbcIndexing abcIndex = swf.getAbcIndex();
abcIndex.selectAbc(selectedAbcContainer.getABC());
ActionScript3Parser parser = new ActionScript3Parser(abcIndex);
((Tag) selectedAbcContainer).setModified(true);
DottedChain dc = new DottedChain(pkgParts);
String script = "package " + dc.toPrintableString(true) + " {"
+ (parentClassName.isEmpty() ? "" : "import " + parentClassName + ";")
+ "public class " + IdentifiersDeobfuscation.printIdentifier(true, classSimpleName) + (parentClassName.isEmpty() ? "" : " extends " + parentClassName) + " {"
+ " }"
+ "}";
parser.addScript(script, fileName, 0, 0, swf.getDocumentClass());
} catch (IOException | InterruptedException | AVM2ParseException | CompilationException ex) {
Logger.getLogger(TagTreeContextMenu.class.getName()).log(Level.SEVERE, "Error during script compilation", ex);
}
((Tag) selectedAbcContainer).setModified(true);
}
}
if (selectedSymbolClass == null) {