diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java index 35ae99dfa..d8742b45a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java @@ -288,4 +288,22 @@ public final class Matrix implements Cloneable { public double getTotalScaleY() { return Math.sqrt(rotateSkew1 * rotateSkew1 + scaleY * scaleY); } + + private int fromFloat(double f){ + return (int)(f * (1 << 16)); + } + + public MATRIX toMATRIX(){ + MATRIX result = new MATRIX(); + + result.translateX = (int)translateX; + result.translateY = (int)translateY; + result.hasRotate = true; + result.hasScale = true; + result.scaleX = fromFloat(scaleX); + result.scaleY = fromFloat(scaleY); + result.rotateSkew0 = fromFloat(rotateSkew0); + result.rotateSkew1 = fromFloat(rotateSkew1); + return result; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java index d1c8d741f..b36988b2d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java @@ -409,4 +409,15 @@ public class PlaceObject2Tag extends CharacterIdTag implements Container, PlaceO return null; } } + + @Override + public void writeTagWithMatrix(SWFOutputStream sos, MATRIX m) throws IOException { + MATRIX old=matrix; + matrix = m; + boolean mod=isModified(); + setModified(true); + super.writeTag(sos); + setModified(mod); + matrix = old; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java index c4b2bd08f..9613d5abc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java @@ -538,4 +538,15 @@ public class PlaceObject3Tag extends CharacterIdTag implements Container, PlaceO return null; } } + + @Override + public void writeTagWithMatrix(SWFOutputStream sos, MATRIX m) throws IOException { + MATRIX old=matrix; + matrix = m; + boolean mod=isModified(); + setModified(true); + super.writeTag(sos); + setModified(mod); + matrix = old; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java index c202a312e..14bbb5895 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java @@ -541,4 +541,15 @@ public class PlaceObject4Tag extends CharacterIdTag implements Container, PlaceO return null; } } + + @Override + public void writeTagWithMatrix(SWFOutputStream sos, MATRIX m) throws IOException { + MATRIX old=matrix; + matrix = m; + boolean mod=isModified(); + setModified(true); + super.writeTag(sos); + setModified(mod); + matrix = old; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObjectTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObjectTag.java index 4d2b403ba..ccb4b9b24 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObjectTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObjectTag.java @@ -218,4 +218,17 @@ public class PlaceObjectTag extends CharacterIdTag implements PlaceObjectTypeTag public CLIPACTIONS getClipActions() { return null; } + + @Override + public void writeTagWithMatrix(SWFOutputStream sos, MATRIX m) throws IOException { + MATRIX old=matrix; + matrix = m; + boolean mod=isModified(); + setModified(true); + super.writeTag(sos); + setModified(mod); + matrix = old; + } + + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/PlaceObjectTypeTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/PlaceObjectTypeTag.java index 47995a2c9..f3081e9b4 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/PlaceObjectTypeTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/PlaceObjectTypeTag.java @@ -12,14 +12,17 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.tags.base; +import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.types.CLIPACTIONS; import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.MATRIX; import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.decompiler.flash.types.filters.FILTER; +import java.io.IOException; import java.util.List; /** @@ -61,4 +64,6 @@ public interface PlaceObjectTypeTag { public int getRatio(); public CLIPACTIONS getClipActions(); + + public void writeTagWithMatrix(SWFOutputStream sos,MATRIX m) throws IOException; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/MATRIX.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/MATRIX.java index 2c63db718..49a5e206c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/MATRIX.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/MATRIX.java @@ -16,6 +16,7 @@ */ package com.jpexs.decompiler.flash.types; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.types.annotations.Calculated; import com.jpexs.decompiler.flash.types.annotations.Conditional; import com.jpexs.decompiler.flash.types.annotations.SWFType; @@ -107,6 +108,8 @@ public class MATRIX implements Serializable { return "[MATRIX scale:" + getScaleXFloat() + "," + getScaleYFloat() + ", rotate:" + getRotateSkew0Float() + "," + getRotateSkew1Float() + ", translate:" + translateX + "," + translateY + "]"; } + + private float toFloat(int i) { return ((float) i) / (1 << 16); } @@ -160,21 +163,7 @@ public class MATRIX implements Serializable { public int getScaleY() { return (hasScale ? (scaleY) : (1 << 16)); } - - public MATRIX merge(MATRIX m) { - MATRIX ret = new MATRIX(); - ret.translateX = m.translateX + this.translateX; - ret.translateY = m.translateY + this.translateY; - - ret.scaleX = (m.hasScale ? m.scaleX : 1) * (this.hasScale ? this.scaleX : 1); - ret.scaleY = (m.hasScale ? m.scaleY : 1) * (this.hasScale ? this.scaleY : 1); - ret.rotateSkew0 = m.rotateSkew0 + this.rotateSkew0; - ret.rotateSkew1 = m.rotateSkew1 + this.rotateSkew1; - ret.hasScale = true; - ret.hasRotate = true; - return ret; - } - + public boolean isEmpty() { return (translateX == 0) && (translateY == 0) && (!hasRotate) && (!hasScale); } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index d4aa6dbb1..eb6466e71 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -1075,6 +1075,21 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec } } + public void gotoFrame(int frame){ + TreeItem treeItem = (TreeItem) tagTree.getLastSelectedPathComponent(); + if (treeItem == null) { + return; + } + if(treeItem instanceof Timelined){ + Timelined t = (Timelined)treeItem; + Timeline tim = t.getTimeline(); + Frame f = ((TagTreeModel)tagTree.getModel()).getFrame(treeItem.getSwf(), t, frame); + if(f!=null){ + setTagTreeSelectedNode(f); + } + } + } + public void gotoDocumentClass(SWF swf) { if (swf == null) { return; diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index a3a4733f6..b5797114c 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.parser.ActionParseException; import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; import com.jpexs.decompiler.flash.gui.player.MediaDisplay; import com.jpexs.decompiler.flash.gui.player.PlayerControls; @@ -30,6 +31,7 @@ import com.jpexs.decompiler.flash.tags.DefineBitsTag; import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; import com.jpexs.decompiler.flash.tags.DefineTextTag; import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; import com.jpexs.decompiler.flash.tags.DoActionTag; @@ -280,7 +282,7 @@ public class PreviewPanel extends JSplitPane implements ActionListener { flashPlayPanel.add(bottomPanel, BorderLayout.SOUTH);*/ JPanel flashPlayPanel2 = new JPanel(new BorderLayout()); flashPlayPanel2.add(flashPlayPanel, BorderLayout.CENTER); - flashPlayPanel2.add(new PlayerControls(flashPanel), BorderLayout.SOUTH); + flashPlayPanel2.add(new PlayerControls(mainPanel,flashPanel), BorderLayout.SOUTH); leftComponent = flashPlayPanel2; } else { JPanel swtPanel = new JPanel(new BorderLayout()); @@ -300,7 +302,7 @@ public class PreviewPanel extends JSplitPane implements ActionListener { JPanel previewCnt = new JPanel(new BorderLayout()); imagePanel = new ImagePanel(); previewCnt.add(imagePanel, BorderLayout.CENTER); - previewCnt.add(imagePlayControls = new PlayerControls(imagePanel), BorderLayout.SOUTH); + previewCnt.add(imagePlayControls = new PlayerControls(mainPanel,imagePanel), BorderLayout.SOUTH); imagePlayControls.setMedia(imagePanel); previewPanel.add(previewCnt, BorderLayout.CENTER); JLabel prevIntLabel = new HeaderLabel(mainPanel.translate("swfpreview.internal")); @@ -509,6 +511,10 @@ public class PreviewPanel extends JSplitPane implements ActionListener { if (tagObj instanceof DefineSoundTag) { frameCount = 1; } + + if(tagObj instanceof DefineSpriteTag){ + frameCount = ((DefineSpriteTag)tagObj).frameCount; + } byte[] data; try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { @@ -588,6 +594,16 @@ public class PreviewPanel extends JSplitPane implements ActionListener { new ShowFrameTag(swf).writeTag(sos2); } else { + boolean isSprite=false; + if(tagObj instanceof DefineSpriteTag){ + isSprite = true; + } + int chtId = 0; + if (tagObj instanceof CharacterTag) { + chtId = ((CharacterTag) tagObj).getCharacterId(); + } + + if (tagObj instanceof DefineBitsTag) { JPEGTablesTag jtt = swf.jtt; if (jtt != null) { @@ -598,16 +614,16 @@ public class PreviewPanel extends JSplitPane implements ActionListener { Set needed = new HashSet<>(); ((Tag) tagObj).getNeededCharactersDeep(needed); for (int n : needed) { + if(isSprite && chtId == n){ + continue; + } classicTag(swf.characters.get(n)).writeTag(sos2); } } classicTag((Tag) tagObj).writeTag(sos2); - int chtId = 0; - if (tagObj instanceof CharacterTag) { - chtId = ((CharacterTag) tagObj).getCharacterId(); - } + MATRIX mat = new MATRIX(); mat.hasRotate = false; @@ -804,6 +820,27 @@ public class PreviewPanel extends JSplitPane implements ActionListener { new ShowFrameTag(swf).writeTag(sos2); first = false; } + } else if(tagObj instanceof DefineSpriteTag){ + DefineSpriteTag s=(DefineSpriteTag)tagObj; + Tag lastTag = null; + for(Tag t:s.subTags){ + if(t instanceof EndTag){ + break; + } else if(t instanceof PlaceObjectTypeTag){ + PlaceObjectTypeTag pt=(PlaceObjectTypeTag)t; + MATRIX m = pt.getMatrix(); + MATRIX m2 = new Matrix(m).preConcatenate(new Matrix(mat)).toMATRIX(); + pt.writeTagWithMatrix(sos2, m2); + lastTag = t; + } + else{ + t.writeTag(sos2); + lastTag = t; + } + } + if(!s.subTags.isEmpty() && (lastTag!=null)&&(!(lastTag instanceof ShowFrameTag))){ + new ShowFrameTag(swf).writeTag(sos2); + } } else { new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); new ShowFrameTag(swf).writeTag(sos2); @@ -823,7 +860,6 @@ public class PreviewPanel extends JSplitPane implements ActionListener { sos.write(data); fos.flush(); } - if (flashPanel != null) { flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, frameRate); } diff --git a/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java b/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java index 87b8aa9c9..bcf3fc84e 100644 --- a/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java @@ -100,7 +100,10 @@ public class FlashPlayerPanel extends Panel implements Closeable, MediaDisplay { @Override public synchronized int getTotalFrames() { - return flash.getTotalFrames(); + if(flash.getReadyState() == 4){ + return flash.getTotalFrames(); + } + return 0; } @Override @@ -166,11 +169,11 @@ public class FlashPlayerPanel extends Panel implements Closeable, MediaDisplay { public synchronized void displaySWF(String flashName, Color bgColor, int frameRate) { zoom = 1.0; - //this.frameRate = frameRate; + this.frameRate = frameRate; if (bgColor != null) { setBackground(bgColor); } - flash.setMovie(flashName); + flash.setMovie(flashName); //play stopped = false; diff --git a/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java b/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java index f0290b954..12c541e0e 100644 --- a/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java +++ b/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java @@ -17,11 +17,14 @@ package com.jpexs.decompiler.flash.gui.player; import com.jpexs.decompiler.flash.gui.AppStrings; +import com.jpexs.decompiler.flash.gui.MainPanel; import com.jpexs.decompiler.flash.gui.View; import java.awt.BorderLayout; import java.awt.Color; +import java.awt.Cursor; import java.awt.Dimension; import java.awt.FlowLayout; +import java.awt.Font; import java.awt.Image; import java.awt.Insets; import java.awt.Toolkit; @@ -34,9 +37,13 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.awt.font.TextAttribute; import java.awt.image.BufferedImage; import java.io.IOException; import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; import java.util.Timer; import java.util.TimerTask; import javax.swing.BoxLayout; @@ -46,6 +53,7 @@ import javax.swing.JColorChooser; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JProgressBar; +import javax.swing.SwingConstants; /** * @@ -68,7 +76,9 @@ public class PlayerControls extends JPanel implements ActionListener { private JProgressBar progress; private final Timer timer; private final JLabel timeLabel; + private final JLabel frameLabel; private final JLabel totalTimeLabel; + private final JLabel totalFrameLabel; private static final Icon pauseIcon = View.getIcon("pause16"); private static final Icon playIcon = View.getIcon("play16"); @@ -83,7 +93,20 @@ public class PlayerControls extends JPanel implements ActionListener { public static final int ZOOM_DECADE_STEPS = 10; public static final double ZOOM_MULTIPLIER = Math.pow(10, 1.0 / ZOOM_DECADE_STEPS); - public PlayerControls(MediaDisplay display) { + private static String underline(String s){ + return ""+s+""; + } + + private static Font underlinedFont=null; + private static Font notUnderlinedFont=null; + static{ + notUnderlinedFont = new JLabel().getFont(); + Map fontAttributes = new HashMap(); + fontAttributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); + underlinedFont= notUnderlinedFont.deriveFont(fontAttributes); + } + + public PlayerControls(final MainPanel mainPanel,MediaDisplay display) { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); @@ -137,10 +160,59 @@ public class PlayerControls extends JPanel implements ActionListener { playbackControls = new JPanel(); this.display = display; JPanel controlPanel = new JPanel(new BorderLayout()); - timeLabel = new JLabel("00:00.00"); - totalTimeLabel = new JLabel("00:00.00"); - controlPanel.add(timeLabel, BorderLayout.WEST); - controlPanel.add(totalTimeLabel, BorderLayout.EAST); + frameLabel = new JLabel("0",SwingConstants.RIGHT); + frameLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + frameLabel.addMouseMotionListener(new MouseMotionAdapter() { + + @Override + public void mouseMoved(MouseEvent e) { + + } + + }); + + Dimension min=new Dimension(frameLabel.getFontMetrics(notUnderlinedFont).stringWidth("000"),frameLabel.getPreferredSize().height); + frameLabel.setMinimumSize(min); + frameLabel.setPreferredSize(min); + + frameLabel.addMouseListener(new MouseAdapter() { + + @Override + public void mouseEntered(MouseEvent e) { + frameLabel.setFont(underlinedFont); + } + + @Override + public void mouseExited(MouseEvent e) { + frameLabel.setFont(notUnderlinedFont); + } + + + + + + @Override + public void mouseClicked(MouseEvent e) { + int gotoFrame = PlayerControls.this.display.getCurrentFrame(); + if(gotoFrame>0){ + mainPanel.gotoFrame(gotoFrame); + } + } + + }); + timeLabel = new JLabel("(00:00.00)"); + totalTimeLabel = new JLabel("(00:00.00)"); + totalFrameLabel = new JLabel("0"); + + JPanel currentPanel=new JPanel(new FlowLayout()); + currentPanel.add(frameLabel); + currentPanel.add(timeLabel); + JPanel totalPanel=new JPanel(new FlowLayout()); + totalPanel.add(totalFrameLabel); + totalPanel.add(totalTimeLabel); + + controlPanel.add(currentPanel, BorderLayout.WEST); + controlPanel.add(totalPanel, BorderLayout.EAST); playbackControls.setLayout(new BoxLayout(playbackControls, BoxLayout.Y_AXIS)); JPanel buttonsPanel = new JPanel(new FlowLayout()); @@ -230,9 +302,11 @@ public class PlayerControls extends JPanel implements ActionListener { progress.setValue(currentFrame); progress.setIndeterminate(false); } - if (frameRate != 0) { - timeLabel.setText(formatMs((currentFrame * 1000) / frameRate)); - totalTimeLabel.setText(formatMs(((totalFrames - 1) * 1000) / frameRate)); + frameLabel.setText((""+(currentFrame+1))); + totalFrameLabel.setText(""+totalFrames); + if (frameRate != 0) { + timeLabel.setText("("+formatMs((currentFrame * 1000) / frameRate)+")"); + totalTimeLabel.setText("("+formatMs(((totalFrames - 1) * 1000) / frameRate)+")"); } if (totalFrames <= 1 && playbackControls.isVisible()) { playbackControls.setVisible(false); diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java index bb69f4237..3a3512f89 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java @@ -305,6 +305,63 @@ public class TagTreeModel implements TreeModel { return null; } + + public TreeItem getFolderNode(SWF swf,String folderType) { + int childCount = getChildCount(swf); + for (int i = 0; i < childCount; i++) { + TreeItem child = getChild(swf, i); + if (child instanceof FolderItem) { + FolderItem folder = (FolderItem) child; + if (folder.getName().equals(folderType)) { + return folder; + } + } + } + + return null; + } + + + private Frame searchForFrame(Object parent,SWF swf,Timelined t, int frame){ + int childCount = getChildCount(parent); + Frame lastVisibleFrame = null; + for (int i = 0; i < childCount; i++) { + TreeItem child = getChild(parent, i); + if((child instanceof DefineSpriteTag)&& child==t){ + Frame si=searchForFrame(child, swf, t,frame); + if(si!=null){ + return si; + } + } + if(child instanceof Frame){ + Frame f = (Frame)child; + if(f.frame<=frame){ + lastVisibleFrame = f; + } + } + if (child instanceof FolderItem) { + FolderItem folder = (FolderItem) child; + if (folder.getName().equals(FOLDER_FRAMES) && t==swf) { + Frame si=searchForFrame(folder, swf, t,frame); + if(si!=null){ + return si; + } + } + if(folder.getName().equals(FOLDER_SPRITES)){ + Frame si=searchForFrame(folder, swf, t,frame); + if(si!=null){ + return si; + } + } + } + } + return lastVisibleFrame; + } + + public Frame getFrame(SWF swf,Timelined t, int frame) { + return searchForFrame(swf, swf, t,frame); + } + private List searchTag(TreeItem obj, TreeItem parent, List path) { List ret = null;