diff --git a/CHANGELOG.md b/CHANGELOG.md index ac7cd1540..c03e28ebe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. - [#1897] Close menu button without selecting specific item - Reading UI32 values - Parsing obfuscated namespaces with hash character "#" +- Tag dependency checking ### Changed - Quick search needs minimum of 3 characters 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 cb849deb0..6bde9d89c 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 @@ -612,19 +612,8 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable { if (tim == null) { return needed2; } - ReadOnlyTagList tags = tim.getTags(); - for (int i = tags.indexOf(this) - 1; i >= -1; i--) { - if (i == -1) { - if (tim instanceof SWF) { - break; - } else { - Timelined parent = ((Tag) tim).getTimelined(); - tags = parent.getTags(); - i = tags.indexOf((Tag) tim); - tim = parent; - continue; - } - } + ReadOnlyTagList tags = tim.getTags(); + for (int i = tags.indexOf(this) - 1; i >= 0; i--) { if (tags.get(i) instanceof CharacterTag) { int charId = ((CharacterTag) tags.get(i)).getCharacterId(); needed2.remove(charId); diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 779de7401..3dcf687e6 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -282,6 +282,8 @@ import javax.swing.tree.TreePath; import jsyntaxpane.DefaultSyntaxKit; import com.jpexs.decompiler.flash.Bundle; import com.jpexs.decompiler.flash.gui.tagtree.FilteredTreeModel; +import com.jpexs.decompiler.flash.tags.StartSoundTag; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; import com.jpexs.decompiler.flash.treeitems.Openable; import java.awt.event.ActionListener; import javax.swing.tree.TreeModel; @@ -369,10 +371,10 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se private final JPersistentSplitPane splitPane2; - private JPanel detailPanel; + private JPanel detailPanel; private QuickTreeFindPanel quickTreeFindPanel; - + private QuickTreeFindPanel quickTagListFindPanel; private ABCPanel abcPanel; @@ -417,7 +419,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se private List> unfilteredTreeExpandedNodes = new ArrayList<>(); private List> unfilteredTagListExpandedNodes = new ArrayList<>(); - + public void savePins() { pinsPanel.save(); } @@ -437,19 +439,19 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public void unpinItem(TreeItem item) { pinsPanel.removeItem(item); } - + public void unpinOthers(TreeItem item) { pinsPanel.removeOthers(item); } - + public void pinItem(TreeItem item) { pinsPanel.pin(item); } - + public int getPinCount() { return pinsPanel.getPinCount(); } - + public boolean isPinned(TreeItem item) { return pinsPanel.isPinned(item); } @@ -514,7 +516,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se getCurrentTree().scrollPathToVisible(path); repaintTree(); } - + public void hideQuickTreeFind() { quickTreeFindPanel.setVisible(false); quickTagListFindPanel.setVisible(false); @@ -1026,7 +1028,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se displayPanel.add(headerPanel, CARDHEADER); displayPanel.add(new JPanel(), CARDEMPTYPANEL); - showCard(CARDEMPTYPANEL); + showCard(CARDEMPTYPANEL); LazyCardLayout treePanelLayout = new LazyCardLayout(); treePanelLayout.registerLayout(createResourcesViewCard(), RESOURCES_VIEW); @@ -1036,8 +1038,6 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se //treePanel.add(searchPanel, BorderLayout.SOUTH); //searchPanel.setVisible(false); - - JPanel rightPanel = new JPanel(new BorderLayout()); rightPanel.add(displayPanel, BorderLayout.CENTER); pinsPanel = new PinsPanel(this); @@ -1076,7 +1076,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } @Override - public void keyPressed(KeyEvent e) { + public void keyPressed(KeyEvent e) { handleTreeKeyPressed(e); if ((e.getKeyCode() == 'G') && (e.isControlDown())) { SWF swf = getCurrentSwf(); @@ -1140,7 +1140,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } else if (e.getAction() == CollectionChangedAction.ADD) { OpenableList list = e.getNewItem(); if (!list.isBundle() && list.items.size() == 1) { - tagTree.expandPath(tagTree.getFullModel().getTreePath(list.get(0))); + tagTree.expandPath(tagTree.getFullModel().getTreePath(list.get(0))); } else { tagTree.expandPath(tagTree.getFullModel().getTreePath(list)); } @@ -1221,7 +1221,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se refreshTree(); } }); - } + } public void resetAllTimelines() { List openableLists = new ArrayList<>(openables); @@ -1272,7 +1272,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se gcClipboard(); reload(false); View.expandTreeNodes(tagTree, expandedNodes); - doFilter(); + doFilter(); pinsPanel.load(); } @@ -1291,8 +1291,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se gcClipboard(); reload(false); - View.expandTreeNodes(getCurrentTree(), expandedNodes); - doFilter(); + View.expandTreeNodes(getCurrentTree(), expandedNodes); + doFilter(); pinsPanel.load(); } @@ -1367,7 +1367,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (!isWelcomeScreen && openables.isEmpty()) { showContentPanelCard(WELCOME_PANEL); - isWelcomeScreen = true; + isWelcomeScreen = true; quickTagListFindPanel.setVisible(false); quickTreeFindPanel.setVisible(false); doFilter(); @@ -1557,7 +1557,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se tagTree.updateUI(); } } - + private boolean isFilterEmpty(String filter) { return filter.trim().length() < 3; } @@ -1566,35 +1566,34 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se TreeModel model = tree.getModel(); String oldFilter = ""; if (model instanceof FilteredTreeModel) { - oldFilter = ((FilteredTreeModel)model).getFilter(); - } + oldFilter = ((FilteredTreeModel) model).getFilter(); + } String newFilter = findPanel.getFilter(); - - + if (isFilterEmpty(oldFilter)) { unfilteredExpandedNodes.clear();; - unfilteredExpandedNodes.addAll(View.getExpandedNodes(tree)); + unfilteredExpandedNodes.addAll(View.getExpandedNodes(tree)); } - + if (oldFilter.trim().equals(newFilter.trim())) { return; - } - - tree.setModel(new FilteredTreeModel(newFilter, tree.getFullModel())); + } + + tree.setModel(new FilteredTreeModel(newFilter, tree.getFullModel())); if (!isFilterEmpty(newFilter)) { for (int i = 0; i < tree.getRowCount(); i++) { tree.expandRow(i); } } else { - tree.setModel(tree.getFullModel()); + tree.setModel(tree.getFullModel()); View.expandTreeNodes(tree, unfilteredExpandedNodes); } } - + public void doFilter() { - View.checkAccess(); + View.checkAccess(); doFilter(tagTree, quickTreeFindPanel, unfilteredTreeExpandedNodes); - doFilter(tagListTree, quickTagListFindPanel, unfilteredTagListExpandedNodes); + doFilter(tagListTree, quickTagListFindPanel, unfilteredTagListExpandedNodes); } public void renameIdentifier(SWF swf, String identifier) throws InterruptedException { @@ -5374,14 +5373,44 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se private static void calculateMissingNeededCharacters(Map> neededMap, Map> missingNeededCharacters, Timelined tim) { List tags = tim.getTags().toArrayList(); + Map> nestedTags = new HashMap<>(); for (Tag t : tags) { - Set needed = new LinkedHashSet<>(); - t.getNeededCharactersDeep(needed); - neededMap.put(t, needed); - missingNeededCharacters.put(t, t.getMissingNeededCharacters(needed)); - if (t instanceof DefineSpriteTag) { - calculateMissingNeededCharacters(neededMap, missingNeededCharacters, (DefineSpriteTag) t); + if ((t instanceof CharacterIdTag) && !(t instanceof CharacterTag) && !(t instanceof SoundStreamHeadTypeTag)) { + int characterId = ((CharacterIdTag) t).getCharacterId(); + if (!nestedTags.containsKey(characterId)) { + nestedTags.put(characterId, new ArrayList<>()); + } + nestedTags.get(characterId).add((CharacterIdTag) t); } + + if (!(t instanceof CharacterTag) && !(t instanceof SoundStreamHeadTypeTag)) { + int characterId = -1; + if (t instanceof CharacterIdTag) { + characterId = ((CharacterIdTag) t).getCharacterId(); + } + Set needed = new LinkedHashSet<>(); + t.getNeededCharactersDeep(needed); + neededMap.put(t, needed); + if ((t instanceof CharacterIdTag) && !(t instanceof PlaceObjectTypeTag)) { + needed = new HashSet<>(); + needed.add(characterId); + } + + if ((t instanceof PlaceObjectTypeTag) && (characterId != -1) && nestedTags.containsKey(characterId)) { + for (CharacterIdTag n : nestedTags.get(characterId)) { + ((Tag) n).getNeededCharactersDeep(needed); + } + } + + missingNeededCharacters.put(t, t.getMissingNeededCharacters(needed)); + if (characterId != -1 && tim.getTimeline().swf.getCharacter(characterId) == null) { + missingNeededCharacters.get(t).add(characterId); + } + + } + /*if (t instanceof DefineSpriteTag) { + calculateMissingNeededCharacters(neededMap, missingNeededCharacters, (DefineSpriteTag) t); + }*/ } } diff --git a/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeCellRenderer.java b/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeCellRenderer.java index eda30bb97..a902cbdb4 100644 --- a/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeCellRenderer.java +++ b/src/com/jpexs/decompiler/flash/gui/taglistview/TagListTreeCellRenderer.java @@ -16,12 +16,15 @@ */ package com.jpexs.decompiler.flash.gui.taglistview; +import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.gui.AppStrings; import com.jpexs.decompiler.flash.gui.View; import com.jpexs.decompiler.flash.gui.tagtree.AbstractTagTree; import com.jpexs.decompiler.flash.gui.tagtree.AbstractTagTreeModel; import com.jpexs.decompiler.flash.gui.tagtree.TagTree; import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.timeline.Frame; import com.jpexs.decompiler.flash.treeitems.OpenableList; import com.jpexs.decompiler.flash.treeitems.TreeItem; @@ -49,7 +52,7 @@ public class TagListTreeCellRenderer extends DefaultTreeCellRenderer { private Font plainFont; private Font boldFont; - + private boolean semiTransparent = false; public TagListTreeCellRenderer() { @@ -57,14 +60,14 @@ public class TagListTreeCellRenderer extends DefaultTreeCellRenderer { setUI(new BasicLabelUI()); setOpaque(false); setBackgroundNonSelectionColor(Color.white); - } - } - + } + } + @Override protected void paintComponent(Graphics g) { super.paintComponent(g); - - if (semiTransparent) { + + if (semiTransparent) { if (getIcon() != null) { Color color = getBackground(); Graphics2D g2d = (Graphics2D) g; @@ -73,9 +76,7 @@ public class TagListTreeCellRenderer extends DefaultTreeCellRenderer { g2d.fillRect(0, 0, getWidth(), getHeight()); } } - } - - + } @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { @@ -84,7 +85,7 @@ public class TagListTreeCellRenderer extends DefaultTreeCellRenderer { val = (TreeItem) value; } - if (val != null && !(val instanceof OpenableList) && val.getOpenable()== null) { + if (val != null && !(val instanceof OpenableList) && val.getOpenable() == null) { // SWF was closed value = null; } @@ -103,10 +104,10 @@ public class TagListTreeCellRenderer extends DefaultTreeCellRenderer { boolean isModified; if (val instanceof Frame) { - isModified = ((Frame)val).isAllInnerTagsModified(); + isModified = ((Frame) val).isAllInnerTagsModified(); } else { isModified = val.isModified(); - } + } if (isModified) { if (boldFont == null) { Font font = getFont(); @@ -127,11 +128,11 @@ public class TagListTreeCellRenderer extends DefaultTreeCellRenderer { setToolTipText(null); AbstractTagTree aTree = (AbstractTagTree) tree; Map> allMissingNeededCharacters = aTree.getMissingNeededCharacters(); - if (allMissingNeededCharacters.containsKey((TreeItem)value)) { + if (allMissingNeededCharacters.containsKey((TreeItem) value)) { Set missingNeededCharacters = allMissingNeededCharacters.get(value); if (!missingNeededCharacters.isEmpty()) { List missingAsStr = new ArrayList<>(); - for (int v:missingNeededCharacters) { + for (int v : missingNeededCharacters) { missingAsStr.add("" + v); } if (missingAsStr.size() == 1) { @@ -139,15 +140,15 @@ public class TagListTreeCellRenderer extends DefaultTreeCellRenderer { } else { lab.setToolTipText(AppStrings.translate("error.missing.characterTag.multi").replace("%tags%",String.join(", ", missingAsStr))); } - lab.setForeground(Color.red); + setForeground(Color.red); } } - + semiTransparent = false; - if (aTree.getMainPanel().isClipboardCut() && aTree.getMainPanel().clipboardContains(val)) { + if (aTree.getMainPanel().isClipboardCut() && aTree.getMainPanel().clipboardContains(val)) { semiTransparent = true; } - + AbstractTagTreeModel model = aTree.getFullModel(); int itemIndex = model.getItemIndex(val); if (itemIndex > 1) { diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java index ab0608ed0..6a78e4a27 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java @@ -82,6 +82,8 @@ import com.jpexs.decompiler.flash.tags.StartSoundTag; import com.jpexs.decompiler.flash.tags.SymbolClassTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.VideoFrameTag; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage; import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage2; @@ -212,7 +214,7 @@ public class TagTree extends AbstractTagTree { List missingAsStr = new ArrayList<>(); for (int v : missingNeededCharacters) { missingAsStr.add("" + v); - } + } if (missingAsStr.size() == 1) { setToolTipText(AppStrings.translate("error.missing.characterTag.single").replace("%tag%", missingAsStr.get(0))); } else {