Added: Changing PlaceObject tag type (PlaceObject, PlaceObject2, ...)

This commit is contained in:
Jindra Petřík
2024-10-01 00:02:17 +02:00
parent e5007f23d0
commit 9793ab0cec
9 changed files with 529 additions and 5 deletions

View File

@@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
but new `spacing "x" NN` and `spacingpair "x" "y" NN` prefix so now
texts are more readable and searchable
- [#2333] Changing Shape tag type (DefineShape, DefineShape2, ...)
- Changing PlaceObject tag type (PlaceObject, PlaceObject2, ...)
### Fixed
- [#2319] AS3 Compound assignments problems in some cases

View File

@@ -0,0 +1,289 @@
/*
* Copyright (C) 2010-2024 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.tags.converters;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.amf.amf3.Amf3Value;
import com.jpexs.decompiler.flash.tags.PlaceObject2Tag;
import com.jpexs.decompiler.flash.tags.PlaceObject3Tag;
import com.jpexs.decompiler.flash.tags.PlaceObject4Tag;
import com.jpexs.decompiler.flash.tags.PlaceObjectTag;
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.types.CLIPACTIONS;
import com.jpexs.decompiler.flash.types.CXFORM;
import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.filters.FILTER;
import com.jpexs.helpers.Helper;
import java.util.List;
/**
*
* @author JPEXS
*/
public class PlaceObjectTypeConverter {
/**
* Gets minimum PlaceObject type number (PlaceObject, PlaceObject2, ...)
* to cover all features that passed placeTag has
* @param placeTag PlaceObject tag
* @return Minimum type number
*/
public int getMinPlaceNum(PlaceObjectTypeTag placeTag) {
int minPlaceNum = 1;
if (placeTag.flagMove()) {
minPlaceNum = 2;
}
if (placeTag.getMatrix() == null) {
minPlaceNum = 2;
}
if (placeTag.getColorTransform() instanceof CXFORMWITHALPHA) {
minPlaceNum = 2;
}
if (placeTag.getClipDepth() > -1) {
minPlaceNum = 2;
}
if (placeTag.getName() != null) {
minPlaceNum = 2;
}
if (placeTag.getRatio() > -1) {
minPlaceNum = 2;
}
if (placeTag.getClassName() != null) {
minPlaceNum = 3;
}
if (placeTag.getBackgroundColor() != null) {
minPlaceNum = 3;
}
if (placeTag.getBitmapCache() != null) {
minPlaceNum = 3;
}
if (placeTag.getBlendMode() > 0) {
minPlaceNum = 3;
}
if (placeTag.getFilters() != null && !placeTag.getFilters().isEmpty()) {
minPlaceNum = 3;
}
if (placeTag.getVisible() != null) {
minPlaceNum = 3;
}
if (placeTag.getAmfData() != null) {
minPlaceNum = 4;
}
return minPlaceNum;
}
/**
* Converts versions of PlaceObject tag (PlaceObject, PlaceObject2, ...)
* @param sourcePlaceTag Source place tag
* @param targetSWF Target SWF
* @param targetPlaceNum Target place type number
* @return Place object of target type
*/
public PlaceObjectTypeTag convertTagType(PlaceObjectTypeTag sourcePlaceTag, SWF targetSWF, int targetPlaceNum) {
ColorTransform colorTransform = sourcePlaceTag.getColorTransform();
CLIPACTIONS clipActions = sourcePlaceTag.getClipActions();
MATRIX matrix = sourcePlaceTag.getMatrix();
Integer bitmapCache = sourcePlaceTag.getBitmapCache();
List<FILTER> filters = sourcePlaceTag.getFilters();
Integer visible = sourcePlaceTag.getVisible();
Amf3Value amfData = sourcePlaceTag.getAmfData();
switch(targetPlaceNum) {
case 1:
PlaceObjectTag place1 = new PlaceObjectTag(targetSWF);
place1.characterId = sourcePlaceTag.getCharacterId();
if (colorTransform != null) {
place1.colorTransform = new CXFORM(colorTransform);
}
place1.depth = sourcePlaceTag.getDepth();
if (matrix != null) {
place1.matrix = new MATRIX(matrix);
} else {
place1.matrix = new MATRIX();
}
return place1;
case 2:
PlaceObject2Tag place2 = new PlaceObject2Tag(targetSWF);
place2.placeFlagMove = sourcePlaceTag.flagMove();
place2.characterId = sourcePlaceTag.getCharacterId();
if (place2.characterId != -1) {
place2.placeFlagHasCharacter = true;
}
if (clipActions != null) {
place2.clipActions = Helper.deepCopy(clipActions);
place2.placeFlagHasClipActions = true;
}
place2.clipDepth = sourcePlaceTag.getClipDepth();
if (place2.clipDepth > -1) {
place2.placeFlagHasClipDepth = true;
}
if (colorTransform != null) {
place2.colorTransform = new CXFORMWITHALPHA(colorTransform);
place2.placeFlagHasColorTransform = true;
}
place2.depth = sourcePlaceTag.getDepth();
if (matrix != null) {
place2.matrix = new MATRIX(matrix);
place2.placeFlagHasMatrix = true;
}
place2.name = sourcePlaceTag.getInstanceName();
if (place2.name != null) {
place2.placeFlagHasName = true;
}
place2.ratio = sourcePlaceTag.getRatio();
if (place2.ratio > -1) {
place2.placeFlagHasRatio = true;
}
return place2;
case 3:
PlaceObject3Tag place3 = new PlaceObject3Tag(targetSWF);
place3.placeFlagMove = sourcePlaceTag.flagMove();
place3.characterId = sourcePlaceTag.getCharacterId();
if (place3.characterId != -1) {
place3.placeFlagHasCharacter = true;
}
if (clipActions != null) {
place3.clipActions = Helper.deepCopy(clipActions);
place3.placeFlagHasClipActions = true;
}
place3.clipDepth = sourcePlaceTag.getClipDepth();
if (place3.clipDepth > -1) {
place3.placeFlagHasClipDepth = true;
}
if (colorTransform != null) {
place3.colorTransform = new CXFORMWITHALPHA(colorTransform);
place3.placeFlagHasColorTransform = true;
}
place3.depth = sourcePlaceTag.getDepth();
if (matrix != null) {
place3.matrix = new MATRIX(matrix);
place3.placeFlagHasMatrix = true;
}
place3.name = sourcePlaceTag.getInstanceName();
if (place3.name != null) {
place3.placeFlagHasName = true;
}
place3.ratio = sourcePlaceTag.getRatio();
if (place3.ratio > -1) {
place3.placeFlagHasRatio = true;
}
place3.className = sourcePlaceTag.getClassName();
if (place3.className != null) {
place3.placeFlagHasClassName = true;
}
place3.backgroundColor = sourcePlaceTag.getBackgroundColor();
if (place3.backgroundColor != null) {
place3.placeFlagOpaqueBackground = true;
}
if (bitmapCache != null) {
place3.bitmapCache = bitmapCache;
place3.placeFlagHasCacheAsBitmap = true;
}
place3.blendMode = sourcePlaceTag.getBlendMode();
if (place3.blendMode > 0) {
place3.placeFlagHasBlendMode = true;
}
if (filters != null && !filters.isEmpty()) {
place3.surfaceFilterList = Helper.deepCopy(filters);
place3.placeFlagHasFilterList = true;
}
if (visible != null) {
place3.visible = visible;
place3.placeFlagHasVisible = true;
}
return place3;
case 4:
PlaceObject4Tag place4 = new PlaceObject4Tag(targetSWF);
place4.placeFlagMove = sourcePlaceTag.flagMove();
place4.characterId = sourcePlaceTag.getCharacterId();
if (place4.characterId != -1) {
place4.placeFlagHasCharacter = true;
}
if (clipActions != null) {
place4.clipActions = Helper.deepCopy(clipActions);
place4.placeFlagHasClipActions = true;
}
place4.clipDepth = sourcePlaceTag.getClipDepth();
if (place4.clipDepth > -1) {
place4.placeFlagHasClipDepth = true;
}
if (colorTransform != null) {
place4.colorTransform = new CXFORMWITHALPHA(colorTransform);
place4.placeFlagHasColorTransform = true;
}
place4.depth = sourcePlaceTag.getDepth();
if (matrix != null) {
place4.matrix = new MATRIX(matrix);
place4.placeFlagHasMatrix = true;
}
place4.name = sourcePlaceTag.getInstanceName();
if (place4.name != null) {
place4.placeFlagHasName = true;
}
place4.ratio = sourcePlaceTag.getRatio();
if (place4.ratio > -1) {
place4.placeFlagHasRatio = true;
}
place4.className = sourcePlaceTag.getClassName();
if (place4.className != null) {
place4.placeFlagHasClassName = true;
}
place4.backgroundColor = sourcePlaceTag.getBackgroundColor();
if (place4.backgroundColor != null) {
place4.placeFlagOpaqueBackground = true;
}
if (bitmapCache != null) {
place4.bitmapCache = bitmapCache;
place4.placeFlagHasCacheAsBitmap = true;
}
place4.blendMode = sourcePlaceTag.getBlendMode();
if (place4.blendMode > 0) {
place4.placeFlagHasBlendMode = true;
}
if (filters != null && !filters.isEmpty()) {
place4.surfaceFilterList = Helper.deepCopy(filters);
place4.placeFlagHasFilterList = true;
}
if (visible != null) {
place4.visible = visible;
place4.placeFlagHasVisible = true;
}
if (amfData != null) {
place4.amfData = Helper.deepCopy(amfData);
}
return place4;
default:
throw new IllegalArgumentException("PlaceNum must be between 1 and 4. Provided: " + targetPlaceNum);
}
}
/**
* Converts versions of PlaceObject tag (PlaceObject, PlaceObject2, ...)
* and place result in the position where original tag was.
* @param placeTag Place tag
* @param targetPlaceNum Target place num
*/
public PlaceObjectTypeTag convertTagType(PlaceObjectTypeTag placeTag, int targetPlaceNum) {
PlaceObjectTypeTag converted = convertTagType(placeTag, placeTag.getSwf(), targetPlaceNum);
converted.setTimelined(placeTag.getTimelined());
placeTag.getTimelined().replaceTag(placeTag, converted);
placeTag.getTimelined().resetTimeline();
return converted;
}
}

View File

@@ -0,0 +1,122 @@
/*
* 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.tags.converters.ShapeTypeConverter;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
/**
*
* @author JPEXS
*/
public class ConvertPlaceObjectTypeDialog extends AppDialog {
private List<JRadioButton> radios = new ArrayList<>();
private int result = 0;
public ConvertPlaceObjectTypeDialog(Window owner, int currentPlaceObjectNum, int min) {
super(owner);
setTitle(translate("dialog.title"));
JPanel radioPanel = new JPanel();
radioPanel.setLayout(new BoxLayout(radioPanel, BoxLayout.Y_AXIS));
ButtonGroup radioGroup = new ButtonGroup();
JButton okButton = new JButton(translate("button.ok"));
for (int i = 1; i <= 4; i++) {
String text = "PlaceObject" + (i > 1 ? "" + i : "");
if (i == min) {
text += " " + translate("minimum");
}
text += " - " + translate("place" + i);
JRadioButton radio = new JRadioButton(text);
radio.setAlignmentX(Component.LEFT_ALIGNMENT);
if (i == currentPlaceObjectNum) {
radio.setSelected(true);
}
final int fi = i;
radio.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
okButton.setEnabled(fi != currentPlaceObjectNum);
}
});
radioPanel.add(radio);
radioGroup.add(radio);
radios.add(radio);
}
Container cnt = getContentPane();
cnt.setLayout(new BorderLayout());
cnt.add(radioPanel, BorderLayout.CENTER);
JPanel buttonsPanel = new JPanel(new FlowLayout());
okButton.setEnabled(false);
okButton.addActionListener(this::okButtonActionPerformed);
JButton cancelButton = new JButton(translate("button.cancel"));
cancelButton.addActionListener(this::cancelButtonActionPerformed);
buttonsPanel.add(okButton);
buttonsPanel.add(cancelButton);
cnt.add(buttonsPanel, BorderLayout.SOUTH);
pack();
View.centerScreen(this);
View.setWindowIcon(this, "placeobject");
setModal(true);
}
private void okButtonActionPerformed(ActionEvent evt) {
result = 0;
for (int i = 0; i < radios.size(); i++) {
if (radios.get(i).isSelected()) {
result = i + 1;
break;
}
}
setVisible(false);
}
private void cancelButtonActionPerformed(ActionEvent evt) {
setVisible(false);
}
public int getResult() {
return result;
}
public int showDialog() {
setVisible(true);
return result;
}
}

View File

@@ -16,8 +16,6 @@
*/
package com.jpexs.decompiler.flash.gui;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.converters.ShapeTypeConverter;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
@@ -52,7 +50,6 @@ public class ConvertShapeTypeDialog extends AppDialog {
JButton okButton = new JButton(translate("button.ok"));
ShapeTypeConverter converter = new ShapeTypeConverter();
for (int i = 1; i <= 4; i++) {
String text = "DefineShape" + (i > 1 ? "" + i : "");
if (i < minForced) {

View File

@@ -0,0 +1,27 @@
# 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 = Convert place object type
place1 = colortransform, depth, character id, matrix
place2 = flag move, clip actions, clip depth, colortransform with alpha, name, ratio
place3 = class name, background color, bitmap cache, blend mode, filters, visible
place4 = AMF data
minimum = (Minimum)
unsupported = (Unsupported)
button.ok = OK
button.cancel = Cancel

View File

@@ -0,0 +1,27 @@
# 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 = Konvertovat typ place objektu
place1 = colortransform, depth, character id, matrix
place2 = flag move, clip actions, clip depth, colortransform s pr\u016fhlednost\u00ed, name, ratio
place3 = class name, background color, bitmap cache, blend mode, filtry, visible
place4 = AMF data
minimum = (Minimum)
unsupported = (Nepodporov\u00e1no)
button.ok = OK
button.cancel = Storno

View File

@@ -1004,4 +1004,5 @@ work.exporting.vsCode = Exporting VS Code
menu.file.export.vsCode = Export to VS Code
#after 21.1.0
contextmenu.convertShapeType = Convert shape type
contextmenu.convertShapeType = Convert shape type
contextmenu.convertPlaceObjectType = Convert place object type

View File

@@ -986,4 +986,5 @@ work.exporting.vsCode = Exportuji VS Code
menu.file.export.vsCode = Exportovat do VS Code
#after 21.1.0
contextmenu.convertShapeType = Konvertovat typ tvaru
contextmenu.convertShapeType = Konvertovat typ tvaru
contextmenu.convertPlaceObjectType = Konvertovat typ place objektu

View File

@@ -42,6 +42,7 @@ 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.ConvertPlaceObjectTypeDialog;
import com.jpexs.decompiler.flash.gui.ConvertShapeTypeDialog;
import com.jpexs.decompiler.flash.gui.Main;
import com.jpexs.decompiler.flash.gui.MainPanel;
@@ -104,6 +105,7 @@ import com.jpexs.decompiler.flash.tags.base.RemoveTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.converters.PlaceObjectTypeConverter;
import com.jpexs.decompiler.flash.tags.converters.ShapeTypeConverter;
import com.jpexs.decompiler.flash.tags.gfx.ExporterInfo;
import com.jpexs.decompiler.flash.timeline.AS2Package;
@@ -340,6 +342,8 @@ public class TagTreeContextMenu extends JPopupMenu {
private JMenuItem collectDepthAsSpritesMenuItem;
private JMenuItem convertShapeTypeMenuItem;
private JMenuItem convertPlaceObjectTypeMenuItem;
private List<TreeItem> items = new ArrayList<>();
@@ -543,6 +547,11 @@ public class TagTreeContextMenu extends JPopupMenu {
convertShapeTypeMenuItem.addActionListener(this::convertShapeTypeActionPerformed);
convertShapeTypeMenuItem.setIcon(View.getIcon("shape16"));
add(convertShapeTypeMenuItem);
convertPlaceObjectTypeMenuItem = new JMenuItem(mainPanel.translate("contextmenu.convertPlaceObjectType"));
convertPlaceObjectTypeMenuItem.addActionListener(this::convertPlaceObjectTypeActionPerformed);
convertPlaceObjectTypeMenuItem.setIcon(View.getIcon("placeobject16"));
add(convertPlaceObjectTypeMenuItem);
addSeparator();
@@ -1075,9 +1084,11 @@ public class TagTreeContextMenu extends JPopupMenu {
}
boolean allSelectedIsShape = true;
boolean allSelectedIsPlaceObject = true;
if (items.isEmpty()) {
allSelectedIsShape = false;
allSelectedIsPlaceObject = false;
}
for (TreeItem item : items) {
@@ -1085,6 +1096,9 @@ public class TagTreeContextMenu extends JPopupMenu {
if (!(item instanceof ShapeTag)) {
allSelectedIsShape = false;
}
if (!(item instanceof PlaceObjectTypeTag)) {
allSelectedIsPlaceObject = false;
}
if (item instanceof Tag) {
Tag tag = (Tag) item;
@@ -1236,6 +1250,7 @@ public class TagTreeContextMenu extends JPopupMenu {
replaceWithTagMenuItem.setVisible(false);
replaceRefsWithTagMenuItem.setVisible(false);
convertShapeTypeMenuItem.setVisible(false);
convertPlaceObjectTypeMenuItem.setVisible(false);
abcExplorerMenuItem.setVisible(false);
cleanAbcMenuItem.setVisible(false);
rawEditMenuItem.setVisible(false);
@@ -1662,6 +1677,10 @@ public class TagTreeContextMenu extends JPopupMenu {
if (allSelectedIsShape) {
convertShapeTypeMenuItem.setVisible(true);
}
if (allSelectedIsPlaceObject) {
convertPlaceObjectTypeMenuItem.setVisible(true);
}
moveTagToMenu.removeAll();
moveTagToWithDependenciesMenu.removeAll();
@@ -2619,6 +2638,46 @@ public class TagTreeContextMenu extends JPopupMenu {
mainPanel.setTagTreeSelectedNode(mainPanel.getCurrentTree(), sh.getSwf().getCharacter(sh.getCharacterId()));
}
}
private void convertPlaceObjectTypeActionPerformed(ActionEvent evt) {
List<TreeItem> itemr = getSelectedItems();
if (itemr.isEmpty()) {
return;
}
int currentPlaceObjectNum = 0;
int min = 0;
PlaceObjectTypeConverter converter = new PlaceObjectTypeConverter();
if (itemr.size() == 1) {
PlaceObjectTypeTag sh = (PlaceObjectTypeTag) itemr.get(0);
currentPlaceObjectNum = sh.getPlaceObjectNum();
min = converter.getMinPlaceNum(sh);
}
ConvertPlaceObjectTypeDialog dialog = new ConvertPlaceObjectTypeDialog(Main.getDefaultDialogsOwner(), currentPlaceObjectNum, min);
int placeNum = dialog.showDialog();
if (placeNum == 0) {
return;
}
PlaceObjectTypeTag lastConverted = null;
for (TreeItem item : itemr) {
PlaceObjectTypeTag pl = (PlaceObjectTypeTag) item;
if (pl.getPlaceObjectNum()== placeNum) {
continue;
}
lastConverted = converter.convertTagType(pl, placeNum);
}
mainPanel.refreshTree();
if (itemr.size() == 1) {
mainPanel.setTagTreeSelectedNode(mainPanel.getCurrentTree(), lastConverted);
}
}
private void replaceRefsWithTagActionPerformed(ActionEvent evt) {
TreeItem itemr = getCurrentItem();