diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b1c26a73..3331e7421 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. ## [Unreleased] ### Added - [#1459], [#1832], [#1849] AS1/2 direct editation - Error dialog when saved value (UI16, SI16, ...) exceeds its limit and this code cannot be saved. +- Attach tag menu (Like DefineScaling grid to DefineSprite, etc.) +- Better tag error handling - these tags now got error icon ### Fixed - Flash viewer - subtract blend mode @@ -19,6 +21,9 @@ All notable changes to this project will be documented in this file. - [#1785] AS1/2 try..catch block in for..in - [#1770] Links in basictag info (like needed/dependent characters) were barely visible on most themes +### Changed +- [#1455] All tag types are now allowed inside DefineSprite + ## [16.0.4] - 2022-11-03 ### Fixed - [#1860] FLA export - EmptyStackException during exporting MorphShape diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java index c7086709d..1b79846e0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFInputStream.java @@ -318,11 +318,11 @@ public class SWFInputStream implements AutoCloseable { private SWF swf; public DumpInfo dumpInfo; - + private byte[] data; private int limit; - + public void addPercentListener(ProgressListener listener) { listeners.add(listener); } @@ -1162,7 +1162,7 @@ public class SWFInputStream implements AutoCloseable { public Tag call() throws Exception { DumpInfo di = dumpInfo; try { - Tag t = resolveTag(tag, level, parallel, skipUnusualTags, lazy); + Tag t = resolveTag(tag, level, parallel, skipUnusualTags, lazy, true); if (dumpInfo != null && t != null) { dumpInfo.name = t.getName(); } @@ -1219,14 +1219,15 @@ public class SWFInputStream implements AutoCloseable { } else if (tag != null) { if (tag.getId() == FileAttributesTag.ID && level == 0) { // FileAttributes if (tag instanceof TagStub) { - tag = resolveTag((TagStub) tag, level, parallel1, skipUnusualTags, lazy); + tag = resolveTag((TagStub) tag, level, parallel1, skipUnusualTags, lazy, true); } FileAttributesTag fileAttributes = (FileAttributesTag) tag; if (fileAttributes.actionScript3) { isAS3 = true; } } - switch (tag.getId()) { + doParse = true; + /*switch (tag.getId()) { case DoActionTag.ID: case DoInitActionTag.ID: doParse = !isAS3; @@ -1254,11 +1255,11 @@ public class SWFInputStream implements AutoCloseable { } else { doParse = true; } - } + }*/ } if (parseTags && !parallel1 && doParse && (tag instanceof TagStub)) { - tag = resolveTag((TagStub) tag, level, parallel, skipUnusualTags, lazy); + tag = resolveTag((TagStub) tag, level, parallel, skipUnusualTags, lazy, true); } DumpInfo di = dumpInfo; if (di != null && tag != null) { @@ -1313,7 +1314,7 @@ public class SWFInputStream implements AutoCloseable { return tags; } - public static Tag resolveTag(TagStub tag, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws InterruptedException { + public static Tag resolveTag(TagStub tag, int level, boolean parallel, boolean skipUnusualTags, boolean lazy, boolean logErrors) throws InterruptedException { Tag ret; ByteArrayRange data = tag.getOriginalRange(); @@ -1609,8 +1610,10 @@ public class SWFInputStream implements AutoCloseable { ret.remainingData = sis.readByteRangeEx(sis.available(), "remaining"); } } catch (IOException ex) { - logger.log(Level.SEVERE, "Error during tag reading. SWF: " + swf.getShortFileName() + " ID: " + tag.getId() + " name: " + tag.getName() + " pos: " + data.getPos(), ex); - ret = new TagStub(swf, tag.getId(), "ErrorTag", data, null); + if (logErrors) { + logger.log(Level.SEVERE, "Error during tag reading. SWF: " + swf.getShortFileName() + " ID: " + tag.getId() + " name: " + tag.getName() + " pos: " + data.getPos(), ex); + } + ret = new TagStub(swf, tag.getId(), "Error", data, null); } ret.forceWriteAsLong = tag.forceWriteAsLong; ret.setTimelined(tag.getTimelined()); @@ -1665,7 +1668,7 @@ public class SWFInputStream implements AutoCloseable { if (resolve) { DumpInfo di = dumpInfo; try { - ret = resolveTag(tagStub, level, parallel, skipUnusualTags, lazy); + ret = resolveTag(tagStub, level, parallel, skipUnusualTags, lazy, true); } catch (Exception ex) { tagDataStream.endDumpLevelUntil(di); logger.log(Level.SEVERE, "Problem in " + timelined.toString(), ex); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/dumpview/DumpInfo.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/dumpview/DumpInfo.java index aa0d6f4d9..61ce6a1c9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/dumpview/DumpInfo.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/dumpview/DumpInfo.java @@ -133,7 +133,7 @@ public class DumpInfo implements TreeItem { SWFInputStream sis = tagStub.getDataStream(); sis.seek(tagStub.getDataPos()); sis.dumpInfo = this; - resolvedTag = SWFInputStream.resolveTag(tagStub, 0, false, true, false); + resolvedTag = SWFInputStream.resolveTag(tagStub, 0, false, true, false, false); } catch (InterruptedException | IOException ex) { Logger.getLogger(DumpInfo.class.getName()).log(Level.SEVERE, null, ex); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/ImageHelper.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/ImageHelper.java index 30d9e216b..77b1a9f03 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/ImageHelper.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/ImageHelper.java @@ -16,10 +16,13 @@ */ package com.jpexs.decompiler.flash.helpers; +import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.tags.enums.ImageFormat; import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.helpers.Helper; import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.io.ByteArrayInputStream; @@ -67,6 +70,14 @@ public class ImageHelper { } } + if (in == null) { + BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = img.getGraphics(); + g.setColor(SWF.ERROR_COLOR); + g.fillRect(0, 0, 1, 1); + return img; + } + int type = in.getType(); if (type != BufferedImage.TYPE_INT_ARGB_PRE && type != BufferedImage.TYPE_INT_RGB) { // convert to ARGB diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG2Tag.java index 42e7f0602..2a01dac4e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG2Tag.java @@ -30,6 +30,7 @@ import com.jpexs.helpers.ByteArrayRange; import com.jpexs.helpers.JpegFixer; import com.jpexs.helpers.SerializableImage; import java.awt.Dimension; +import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -152,7 +153,11 @@ public class DefineBitsJPEG2Tag extends ImageTag implements AloneTag { Logger.getLogger(DefineBitsJPEG2Tag.class.getName()).log(Level.SEVERE, "Failed to get image", ex); } - return null; + SerializableImage img = new SerializableImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = img.getGraphics(); + g.setColor(SWF.ERROR_COLOR); + g.fillRect(0, 0, 1, 1); + return img; } @Override @@ -167,6 +172,6 @@ public class DefineBitsJPEG2Tag extends ImageTag implements AloneTag { Logger.getLogger(DefineBitsJPEG2Tag.class.getName()).log(Level.SEVERE, "Failed to get image dimension", ex); } - return null; + return new Dimension(1, 1); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java index e798324f6..5d94987e3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java @@ -31,6 +31,7 @@ import com.jpexs.helpers.ByteArrayRange; import com.jpexs.helpers.JpegFixer; import com.jpexs.helpers.SerializableImage; import java.awt.Dimension; +import java.awt.Graphics; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.io.ByteArrayInputStream; @@ -244,7 +245,12 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag { } catch (IOException ex) { Logger.getLogger(DefineBitsJPEG3Tag.class.getName()).log(Level.SEVERE, "Failed to get image", ex); } - return null; + + SerializableImage img = new SerializableImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = img.getGraphics(); + g.setColor(SWF.ERROR_COLOR); + g.fillRect(0, 0, 1, 1); + return img; } @Override @@ -261,6 +267,6 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag { Logger.getLogger(DefineBitsJPEG3Tag.class.getName()).log(Level.SEVERE, "Failed to get image dimension", ex); } - return null; + return new Dimension(1, 1); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java index 86ff2a9ea..04d435efa 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java @@ -31,6 +31,7 @@ import com.jpexs.helpers.ByteArrayRange; import com.jpexs.helpers.JpegFixer; import com.jpexs.helpers.SerializableImage; import java.awt.Dimension; +import java.awt.Graphics; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.io.ByteArrayInputStream; @@ -250,7 +251,12 @@ public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag { } catch (IOException ex) { Logger.getLogger(DefineBitsJPEG4Tag.class.getName()).log(Level.SEVERE, "Failed to get image", ex); } - return null; + + SerializableImage img = new SerializableImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = img.getGraphics(); + g.setColor(SWF.ERROR_COLOR); + g.fillRect(0, 0, 1, 1); + return img; } @Override @@ -266,6 +272,6 @@ public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag { Logger.getLogger(DefineBitsJPEG3Tag.class.getName()).log(Level.SEVERE, "Failed to get image dimension", ex); } - return null; + return new Dimension(1, 1); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsTag.java index ba7776516..e7cd329aa 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsTag.java @@ -28,6 +28,7 @@ import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.helpers.ByteArrayRange; import com.jpexs.helpers.SerializableImage; import java.awt.Dimension; +import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -209,7 +210,11 @@ public class DefineBitsTag extends ImageTag implements TagChangedListener { } } - return null; + SerializableImage img = new SerializableImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = img.getGraphics(); + g.setColor(SWF.ERROR_COLOR); + g.fillRect(0, 0, 1, 1); + return img; } @Override @@ -227,7 +232,7 @@ public class DefineBitsTag extends ImageTag implements TagChangedListener { } } - return null; + return new Dimension(1, 1); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java index 0e51df886..3b8893da8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineFont2Tag.java @@ -189,10 +189,12 @@ public class DefineFont2Tag extends FontTag { for (int i = 0; i < numGlyphs; i++) { fontBoundsTable.add(sis.readRECT("rect")); } - int kerningCount = sis.readUI16("kerningCount"); fontKerningTable = new ArrayList<>(); - for (int i = 0; i < kerningCount; i++) { - fontKerningTable.add(sis.readKERNINGRECORD(fontFlagsWideCodes, "record")); + if (sis.available() > 0) { //should always be available, but happened in #1455, god knows why + int kerningCount = sis.readUI16("kerningCount"); + for (int i = 0; i < kerningCount; i++) { + fontKerningTable.add(sis.readKERNINGRECORD(fontFlagsWideCodes, "record")); + } } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java index b88e9a785..981a9011e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java @@ -429,7 +429,7 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable { SWFInputStream tagDataStream = new SWFInputStream(swf, data, getDataPos(), data.length); TagStub copy = new TagStub(swf, getId(), "Unresolved", getOriginalRange(), tagDataStream); copy.forceWriteAsLong = forceWriteAsLong; - return SWFInputStream.resolveTag(copy, 0, false, true, false); + return SWFInputStream.resolveTag(copy, 0, false, true, false, false); } public Tag getOriginalTag() throws InterruptedException, IOException { @@ -437,7 +437,7 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable { SWFInputStream tagDataStream = new SWFInputStream(swf, data, getDataPos(), data.length); TagStub copy = new TagStub(swf, getId(), "Unresolved", getOriginalRange(), tagDataStream); copy.forceWriteAsLong = forceWriteAsLong; - return SWFInputStream.resolveTag(copy, 0, false, true, false); + return SWFInputStream.resolveTag(copy, 0, false, true, false, false); } public boolean canUndo() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/TagStub.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/TagStub.java index 75ef4db76..39cf41b83 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/TagStub.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/TagStub.java @@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.helpers.ByteArrayRange; import java.io.IOException; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -67,4 +68,13 @@ public class TagStub extends Tag { public SWFInputStream getDataStream() { return dataStream; } + + @Override + public String toString() { + Map classes = Tag.getKnownClasses(); + if (classes.containsKey(id)) { + return tagName + " - " + classes.get(id).getName(); + } + return tagName + " [ID = " + id + "]"; + } } diff --git a/src/com/jpexs/decompiler/flash/gui/TreeNodeType.java b/src/com/jpexs/decompiler/flash/gui/TreeNodeType.java index 63e5b40d4..d1518fac5 100644 --- a/src/com/jpexs/decompiler/flash/gui/TreeNodeType.java +++ b/src/com/jpexs/decompiler/flash/gui/TreeNodeType.java @@ -61,5 +61,6 @@ public enum TreeNodeType { PLACE_OBJECT, REMOVE_OBJECT, SCALING_GRID, - END + END, + ERROR } diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index 7d8755f0f..2625b4523 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -902,4 +902,6 @@ contextmenu.clone = Clone #after 16.0.4 error.action.save.valueTooLarge = Code cannot be saved. Binary representation of this script generated by the FFDecs compiler\r\nrequires more room \ than the maximum limit of bytes allowed.\r\nThere is a limit of how many bytes can be stored in an action tag \ -and/or ActionDefineFunction/2.\r\nYou can try to shorten the script and/or the functions it contains and try again. \ No newline at end of file +and/or ActionDefineFunction/2.\r\nYou can try to shorten the script and/or the functions it contains and try again. + +contextmenu.attachTag = Attach tag \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties index dabd577eb..ddbebc926 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties @@ -872,4 +872,6 @@ contextmenu.clone = Klonovat #after 16.0.4 error.action.save.valueTooLarge = K\u00f3d nem\u016f\u017ee b\u00fdt ulo\u017een. Bin\u00e1rn\u00ed reprezentace tohoto skriptu generovan\u00e1 kompil\u00e1torem FFDec\r\nvy\u017eaduje v\u00edce m\u00edsta \ ne\u017e je povolen\u00fd maxim\u00e1ln\u00ed limit po\u010dtu bajt\u016f.\r\nExistuje limit kolik bajt\u016f lze ulo\u017eit do action tagu \ -a/nebo ActionDefineFunction/2.\r\nM\u016f\u017eete zkusit zkr\u00e1tit skript a/nebo funkce, kter\u00e9 obsahuje, a zkusit to znovu. \ No newline at end of file +a/nebo ActionDefineFunction/2.\r\nM\u016f\u017eete zkusit zkr\u00e1tit skript a/nebo funkce, kter\u00e9 obsahuje, a zkusit to znovu. + +contextmenu.attachTag = Nav\u00e1zat tag \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTree.java b/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTree.java index ddea35a64..669486311 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTree.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTree.java @@ -71,6 +71,7 @@ import com.jpexs.decompiler.flash.tags.SoundStreamHeadTag; import com.jpexs.decompiler.flash.tags.StartSound2Tag; import com.jpexs.decompiler.flash.tags.StartSoundTag; import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.TagStub; import com.jpexs.decompiler.flash.tags.VideoFrameTag; import com.jpexs.decompiler.flash.tags.base.ASMSource; import com.jpexs.decompiler.flash.tags.base.ButtonTag; @@ -327,6 +328,10 @@ public abstract class AbstractTagTree extends JTree { return TreeNodeType.END; } + if (t instanceof TagStub) { + return TreeNodeType.ERROR; + } + if (t instanceof Tag) { return TreeNodeType.OTHER_TAG; } diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index 59f93ce46..bd8bc9dbe 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -159,6 +159,8 @@ public class TagTreeContextMenu extends JPopupMenu { private JMenu addTagInsideMenu; + private JMenu attachTagMenu; + private JMenu addTagBeforeMenu; private JMenu addTagAfterMenu; @@ -190,13 +192,11 @@ public class TagTreeContextMenu extends JPopupMenu { private JMenuItem addFramesBeforeMenuItem; private JMenuItem addFramesAfterMenuItem; - + private static final int KIND_MOVETO = 0; private static final int KIND_COPYTO = 1; private static final int KIND_COPYTODEPS = 2; - - public TagTreeContextMenu(final List trees, MainPanel mainPanel) { this.mainPanel = mainPanel; @@ -288,6 +288,10 @@ public class TagTreeContextMenu extends JPopupMenu { addTagInsideMenu.setIcon(View.getIcon("addtag16")); add(addTagInsideMenu); + attachTagMenu = new JMenu(mainPanel.translate("contextmenu.attachTag")); + attachTagMenu.setIcon(View.getIcon("addtag16")); + add(attachTagMenu); + addTagBeforeMenu = new JMenu(mainPanel.translate("contextmenu.addTagBefore")); addTagBeforeMenu.setIcon(View.getIcon("addtag16")); add(addTagBeforeMenu); @@ -589,6 +593,7 @@ public class TagTreeContextMenu extends JPopupMenu { importSwfXmlMenuItem.setVisible(allSelectedIsSwf); closeMenuItem.setVisible(allSelectedIsSwf); addTagInsideMenu.setVisible(false); + attachTagMenu.setVisible(false); addTagBeforeMenu.setVisible(false); addTagAfterMenu.setVisible(false); moveTagToMenu.setVisible(false); @@ -698,6 +703,10 @@ public class TagTreeContextMenu extends JPopupMenu { addAddTagInsideMenuItems(firstItem); addTagInsideMenu.setVisible(addTagInsideMenu.getItemCount() > 0); + attachTagMenu.removeAll(); + addAttachTagMenuItems(firstItem); + attachTagMenu.setVisible(attachTagMenu.getItemCount() > 0); + addTagBeforeMenu.removeAll(); addAddTagBeforeAfterMenuItems(true, addTagBeforeMenu, firstItem, this::addTagBeforeActionPerformed); @@ -823,6 +832,12 @@ public class TagTreeContextMenu extends JPopupMenu { addTagMenu.add(folderMenu); } + private void addAttachTagMenuItems(TreeItem item) { + AddTagActionListener listener = this::attachTagActionPerformed; + List mapped = AbstractTagTree.getMappedTagIdsForClass(item.getClass()); + addAddTagMenuItems(mapped, attachTagMenu, item, listener); + } + private void addAddTagInsideMenuItems(TreeItem item) { AddTagActionListener listener = this::addTagInsideActionPerformed; Map classes = Tag.getKnownClasses(); @@ -840,48 +855,34 @@ public class TagTreeContextMenu extends JPopupMenu { if (item instanceof DefineSpriteTag) { addAddTagMenuItems(AbstractTagTree.getFrameNestedTagIds(), addTagInsideMenu, item, listener); addTagInsideMenu.addSeparator(); - addAddTagMenuItems(AbstractTagTree.getMappedTagIdsForClass(DefineSpriteTag.class), addTagInsideMenu, item, listener); + addTagInsideMenu.add(createOthersMenu(item, listener)); return; } if (item instanceof Frame) { Frame frame = (Frame) item; boolean insideSprite = frame.timeline.timelined instanceof DefineSpriteTag; - if (insideSprite) { - addAddTagMenuItems(AbstractTagTree.getFrameNestedTagIds(), addTagInsideMenu, item, listener); + if (mainPanel.getCurrentView() == MainPanel.VIEW_TAGLIST) { + addAddTagMenuItems(null, addTagInsideMenu, item, listener); + return; } else { - if (mainPanel.getCurrentView() == MainPanel.VIEW_TAGLIST) { - addAddTagMenuItems(null, addTagInsideMenu, item, listener); - return; - } addAddTagMenuItems(AbstractTagTree.getFrameNestedTagIds(), addTagInsideMenu, item, listener); - if (!insideSprite) { - addTagInsideMenu.addSeparator(); - addTagInsideMenu.add(createOthersMenu(item, listener)); - } + addTagInsideMenu.addSeparator(); + addTagInsideMenu.add(createOthersMenu(item, listener)); } return; } if (item instanceof FolderItem) { List allowedTagTypes = new ArrayList<>(TagTree.getSwfFolderItemNestedTagIds(((FolderItem) item).getName(), gfx)); - - Set mappedTagTypes = new LinkedHashSet<>(); - for (int i : allowedTagTypes) { - mappedTagTypes.addAll(AbstractTagTree.getMappedTagIdsForClass(classes.get(i).getCls())); - } addAddTagMenuItems(allowedTagTypes, addTagInsideMenu, item, listener); - if (!allowedTagTypes.isEmpty() && !mappedTagTypes.isEmpty()) { - addTagInsideMenu.addSeparator(); - } - addAddTagMenuItems(new ArrayList(mappedTagTypes), addTagInsideMenu, item, listener); return; } - if (mainPanel.getCurrentView() == MainPanel.VIEW_RESOURCES) { + /*if (mainPanel.getCurrentView() == MainPanel.VIEW_RESOURCES) { List mapped = AbstractTagTree.getMappedTagIdsForClass(item.getClass()); addAddTagMenuItems(mapped, addTagInsideMenu, item, listener); - } + }*/ } private void addAddTagBeforeAfterMenuItems(boolean before, JMenu addTagMenu, TreeItem item, AddTagActionListener listener) { @@ -921,28 +922,17 @@ public class TagTreeContextMenu extends JPopupMenu { } addAddTagMenuItems(AbstractTagTree.getFrameNestedTagIds(), addTagMenu, item, listener); - if (!insideSprite) { - addTagMenu.addSeparator(); - addTagMenu.add(createOthersMenu(item, listener)); - } + addTagMenu.addSeparator(); + addTagMenu.add(createOthersMenu(item, listener)); + return; } if (parent instanceof FolderItem) { List allowedTagTypes = new ArrayList<>(TagTree.getSwfFolderItemNestedTagIds(((FolderItem) parent).getName(), gfx)); - Set mappedTagTypes = new LinkedHashSet<>(); - for (int i : allowedTagTypes) { - mappedTagTypes.addAll(AbstractTagTree.getMappedTagIdsForClass(classes.get(i).getCls())); - } - addAddTagMenuItems(allowedTagTypes, addTagMenu, item, listener); - if (!allowedTagTypes.isEmpty() && !mappedTagTypes.isEmpty()) { - addTagMenu.addSeparator(); - } - addAddTagMenuItems(new ArrayList(mappedTagTypes), addTagMenu, item, listener); - if (!allowedTagTypes.isEmpty() && !mappedTagTypes.isEmpty()) { - addTagMenu.addSeparator(); - } + addAddTagMenuItems(allowedTagTypes, addTagMenu, item, listener); + addTagMenu.addSeparator(); addTagMenu.add(createOthersMenu(item, listener)); return; } @@ -1007,25 +997,7 @@ public class TagTreeContextMenu extends JPopupMenu { } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { Logger.getLogger(TagTreeContextMenu.class.getName()).log(Level.SEVERE, null, ex); - } - boolean allowedInsideSprite = false; - switch (id) { - case ShowFrameTag.ID: - case PlaceObjectTag.ID: - case PlaceObject2Tag.ID: - case PlaceObject3Tag.ID: - case PlaceObject4Tag.ID: - case RemoveObjectTag.ID: - case RemoveObject2Tag.ID: - case StartSoundTag.ID: - case FrameLabelTag.ID: - case SoundStreamHeadTag.ID: - case SoundStreamHead2Tag.ID: - case SoundStreamBlockTag.ID: - case VideoFrameTag.ID: - case EndTag.ID: - allowedInsideSprite = true; - } + } SWF swf = item.getSwf(); Timelined selectedTimelined = null; @@ -1041,29 +1013,16 @@ public class TagTreeContextMenu extends JPopupMenu { } } else if (item instanceof FolderItem) { selectedTimelined = item.getSwf(); - } else if (item instanceof Tag) { - selectedTimelined = item.getSwf(); //mapped tags - selectedTag = (Tag) item; - selectNext = true; } else if (item instanceof SWF) { selectedTimelined = (SWF) item; } - SelectTagPositionDialog selectPositionDialog = new SelectTagPositionDialog(mainPanel.getMainFrame().getWindow(), swf, selectedTag, selectedTimelined, allowedInsideSprite, selectNext); + SelectTagPositionDialog selectPositionDialog = new SelectTagPositionDialog(mainPanel.getMainFrame().getWindow(), swf, selectedTag, selectedTimelined, true, selectNext); if (selectPositionDialog.showDialog() == AppDialog.OK_OPTION) { selectedTimelined = selectPositionDialog.getSelectedTimelined(); selectedTag = selectPositionDialog.getSelectedTag(); try { Tag t = (Tag) cl.getDeclaredConstructor(SWF.class).newInstance(new Object[]{swf}); - - //it's "inside", add mapping - if (AbstractTagTree.getMappedTagIdsForClass(item.getClass()).contains(id)) { - if ((t instanceof CharacterIdTag) && (!(t instanceof CharacterTag)) && (item instanceof CharacterTag)) { - CharacterIdTag chit = (CharacterIdTag) t; - chit.setCharacterId(((CharacterTag) item).getCharacterId()); - } - } - t.setTimelined(selectedTimelined); if (selectedTag == null) { selectedTimelined.addTag(t); @@ -2340,28 +2299,9 @@ public class TagTreeContextMenu extends JPopupMenu { } if (timelined == null) { //should not happen return; - } + } - boolean allowedInsideSprite = false; - switch (t.getId()) { - case ShowFrameTag.ID: - case PlaceObjectTag.ID: - case PlaceObject2Tag.ID: - case PlaceObject3Tag.ID: - case PlaceObject4Tag.ID: - case RemoveObjectTag.ID: - case RemoveObject2Tag.ID: - case StartSoundTag.ID: - case FrameLabelTag.ID: - case SoundStreamHeadTag.ID: - case SoundStreamHead2Tag.ID: - case SoundStreamBlockTag.ID: - case VideoFrameTag.ID: - case EndTag.ID: - allowedInsideSprite = true; - } - - SelectTagPositionDialog dialog = new SelectTagPositionDialog(Main.getDefaultDialogsOwner(), t.getSwf(), t, timelined, allowedInsideSprite, false); + SelectTagPositionDialog dialog = new SelectTagPositionDialog(Main.getDefaultDialogsOwner(), t.getSwf(), t, timelined, true, false); if (dialog.showDialog() == AppDialog.OK_OPTION) { Tag selectedTag = dialog.getSelectedTag(); Timelined selectedTimelined = dialog.getSelectedTimelined(); @@ -2540,4 +2480,36 @@ public class TagTreeContextMenu extends JPopupMenu { } } } + + private void attachTagActionPerformed(ActionEvent evt, TreeItem item, Class cl) { + int id = -1; + try { + id = cl.getDeclaredField("ID").getInt(null); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { + Logger.getLogger(TagTreeContextMenu.class.getName()).log(Level.SEVERE, null, ex); + } + Timelined selectedTimelined = ((Tag) item).getTimelined(); + Tag tagToAttachTo = ((Tag) item); + SWF swf = ((Tag) item).getSwf(); + try { + Tag t = (Tag) cl.getDeclaredConstructor(SWF.class).newInstance(new Object[]{swf}); + + //add mapping + if (AbstractTagTree.getMappedTagIdsForClass(item.getClass()).contains(id)) { + if ((t instanceof CharacterIdTag) && (!(t instanceof CharacterTag)) && (item instanceof CharacterTag)) { + CharacterIdTag chit = (CharacterIdTag) t; + chit.setCharacterId(((CharacterTag) item).getCharacterId()); + } + } + + t.setTimelined(selectedTimelined); + selectedTimelined.addTag(selectedTimelined.indexOfTag(tagToAttachTo) + 1, t); + selectedTimelined.resetTimeline(); + swf.updateCharacters(); + mainPanel.refreshTree(swf); + mainPanel.setTagTreeSelectedNode(mainPanel.getCurrentTree(), t); + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) { + logger.log(Level.SEVERE, null, ex); + } + } } diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java index e715282bf..a0091e981 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeModel.java @@ -146,33 +146,21 @@ public class TagTreeModel extends AbstractTagTreeModel { fireTreeStructureChanged(new TreeModelEvent(this, changedPath)); } - private List getSoundStreams(DefineSpriteTag sprite) { - List ret = new ArrayList<>(); - for (Tag t : sprite.getTags()) { - if (t instanceof SoundStreamHeadTypeTag) { - ret.add((SoundStreamHeadTypeTag) t); - } - } - return ret; - } - - private void createTagList(SWF swf) { - List nodeList = new ArrayList<>(); - List frames = new ArrayList<>(); - List shapes = new ArrayList<>(); - List morphShapes = new ArrayList<>(); - List sprites = new ArrayList<>(); - List buttons = new ArrayList<>(); - List images = new ArrayList<>(); - List fonts = new ArrayList<>(); - List texts = new ArrayList<>(); - List movies = new ArrayList<>(); - List sounds = new ArrayList<>(); - List binaryData = new ArrayList<>(); - List others = new ArrayList<>(); - List emptyFolders = new ArrayList<>(); - Map> mappedTags = new HashMap<>(); - for (Tag t : swf.getTags()) { + private void walkTimelinedTagList(Timelined timelined, + Map> mappedTags, + List shapes, + List morphShapes, + List sprites, + List buttons, + List images, + List fonts, + List texts, + List movies, + List sounds, + List binaryData, + List others + ) { + for (Tag t : timelined.getTags()) { TreeNodeType ttype = TagTree.getTreeNodeType(t); switch (ttype) { case SHAPE: @@ -183,7 +171,7 @@ public class TagTreeModel extends AbstractTagTreeModel { break; case SPRITE: sprites.add(t); - sounds.addAll(getSoundStreams((DefineSpriteTag) t)); + walkTimelinedTagList((DefineSpriteTag) t, mappedTags, shapes, morphShapes, sprites, buttons, images, fonts, texts, movies, sounds, binaryData, others); break; case BUTTON: buttons.add(t); @@ -214,6 +202,12 @@ public class TagTreeModel extends AbstractTagTreeModel { boolean parentFound = false; if ((t instanceof CharacterIdTag) && !(t instanceof CharacterTag)) { CharacterIdTag chit = (CharacterIdTag) t; + SWF swf; + if (timelined instanceof SWF) { + swf = (SWF) timelined; + } else { + swf = ((DefineSpriteTag) timelined).getSwf(); + } if (swf.getCharacter(chit.getCharacterId()) != null) { parentFound = true; if (!mappedTags.containsKey(chit.getCharacterId())) { @@ -229,6 +223,25 @@ public class TagTreeModel extends AbstractTagTreeModel { break; } } + } + + private void createTagList(SWF swf) { + List nodeList = new ArrayList<>(); + List frames = new ArrayList<>(); + List shapes = new ArrayList<>(); + List morphShapes = new ArrayList<>(); + List sprites = new ArrayList<>(); + List buttons = new ArrayList<>(); + List images = new ArrayList<>(); + List fonts = new ArrayList<>(); + List texts = new ArrayList<>(); + List movies = new ArrayList<>(); + List sounds = new ArrayList<>(); + List binaryData = new ArrayList<>(); + List others = new ArrayList<>(); + List emptyFolders = new ArrayList<>(); + Map> mappedTags = new HashMap<>(); + walkTimelinedTagList(swf, mappedTags, shapes, morphShapes, sprites, buttons, images, fonts, texts, movies, sounds, binaryData, others); Timeline timeline = swf.getTimeline(); int frameCount = timeline.getFrameCount();