diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fba159fbb..5b025a1893 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,18 @@ # Change Log All notable changes to this project will be documented in this file. +## [18.4.1] - 2023-04-05 +### Fixed +- [#1993] Incorrect scroll position causing shapes to be hidden +- [#1994] Replace command in commandline with three argument causing replacements file load +- [#1477] Open file (Context menu) with unicode characters, unicode in paths, on Windows +- Starting app with parameters causing wrong GUI init +- [#1991] ConcurrentModificationException on clearing cache thread +- [#1999] AS3 decompilation - XML constructor call with other than string argument + +### Changed +- [#1996] Items are now exported in order of appearance in the tag tree (usually SWF order), previously was it in order of selection + ## [18.4.0] - 2023-03-19 ### Added - AS3 support for logical AND/OR compound operator @@ -2862,6 +2874,7 @@ All notable changes to this project will be documented in this file. ### Added - Initial public release +[18.4.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version18.4.0...version18.4.1 [18.4.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version18.3.6...version18.4.0 [18.3.6]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version18.3.5...version18.3.6 [18.3.5]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version18.3.4...version18.3.5 @@ -3008,6 +3021,12 @@ All notable changes to this project will be documented in this file. [alpha 9]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha8...alpha9 [alpha 8]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha7...alpha8 [alpha 7]: https://github.com/jindrapetrik/jpexs-decompiler/releases/tag/alpha7 +[#1993]: https://www.free-decompiler.com/flash/issues/1993 +[#1994]: https://www.free-decompiler.com/flash/issues/1994 +[#1477]: https://www.free-decompiler.com/flash/issues/1477 +[#1991]: https://www.free-decompiler.com/flash/issues/1991 +[#1999]: https://www.free-decompiler.com/flash/issues/1999 +[#1996]: https://www.free-decompiler.com/flash/issues/1996 [#1888]: https://www.free-decompiler.com/flash/issues/1888 [#1892]: https://www.free-decompiler.com/flash/issues/1892 [#355]: https://www.free-decompiler.com/flash/issues/355 diff --git a/build.xml b/build.xml index db4a475887..94fc8df407 100644 --- a/build.xml +++ b/build.xml @@ -395,7 +395,7 @@ - + -Djava.net.preferIPv4Stack=true -Djna.nosys=true @@ -444,7 +443,7 @@ - + --> @@ -487,18 +486,18 @@ - - + + - - + + diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructIns.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructIns.java index 13d7a5b87e..2d5efd275d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructIns.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/construction/ConstructIns.java @@ -80,7 +80,7 @@ public boolean execute(LocalDataArea lda, AVM2ConstantPool constants, AVM2Instru public static boolean walkXML(GraphTargetItem item, List list) { boolean ret = walkXMLSub(item, list); - if (list.size() == 1) { + if (list.size() == 1 && (list.get(0) instanceof StringAVM2Item)) { return true; } return ret; diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java index 7a928cc804..e4fb32b4b9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java +++ b/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java @@ -22,7 +22,6 @@ import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -43,6 +42,8 @@ public class Cache implements Freed { private Map cache; private Map lastAccessed; + private static final Object instancesLock = new Object(); + private static final List> instances = new ArrayList<>(); public static final int STORAGE_FILES = 1; @@ -54,27 +55,28 @@ public class Cache implements Freed { private final boolean memoryOnly; private final String name; - + private final boolean temporary; - + private static final long CLEAN_INTERVAL = 5 * 1000; //5 seconds - - private static Thread oldCleaner = null; + + private static Thread oldCleaner = null; static { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { - for (WeakReference cw : instances) { - Cache c = cw.get(); - if (c != null) { - c.clear(); - c.free(); + synchronized (instancesLock) { + for (WeakReference cw : instances) { + Cache c = cw.get(); + if (c != null) { + c.clear(); + c.free(); + } } } } - }); } @@ -83,37 +85,41 @@ public static Cache getInstance(boolean weak, boolean memoryOnly, S oldCleaner = new Thread("Old cache cleaner") { @Override public void run() { - while(!Thread.interrupted()) { + while (!Thread.interrupted()) { try { Thread.sleep(CLEAN_INTERVAL); } catch (InterruptedException ex) { return; } try { - clearAllOld(); + clearAllOld(); } catch (Exception cme) { Logger.getLogger(Cache.class.getSimpleName()).log(Level.SEVERE, "Error during clearing cache thread", cme); } } - } + } }; oldCleaner.setDaemon(true); oldCleaner.setPriority(Thread.MIN_PRIORITY); oldCleaner.start(); } Cache instance = new Cache<>(weak, memoryOnly, name, temporary); - instances.add(new WeakReference<>(instance)); + synchronized (instancesLock) { + instances.add(new WeakReference<>(instance)); + } return instance; } private static int storageType = STORAGE_FILES; public static void clearAll() { - for (WeakReference cw : instances) { - Cache c = cw.get(); - if (c != null) { - c.clear(); - c.initCache(); + synchronized (instancesLock) { + for (WeakReference cw : instances) { + Cache c = cw.get(); + if (c != null) { + c.clear(); + c.initCache(); + } } } } @@ -174,7 +180,7 @@ private Cache(boolean weak, boolean memoryOnly, String name, boolean temporary) initCache(); } - public synchronized boolean contains(K key) { + public synchronized boolean contains(K key) { boolean ret = cache.containsKey(key); if (ret) { lastAccessed.put(key, System.currentTimeMillis()); @@ -208,7 +214,7 @@ public synchronized void put(K key, V value) { @Override public boolean isFreeing() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new UnsupportedOperationException("Not supported yet."); } @Override @@ -223,7 +229,7 @@ public Set keys() { ret.addAll(cache.keySet()); return ret; } - + private synchronized int clearOld() { long currentTime = System.currentTimeMillis(); Set keys = new HashSet<>(lastAccessed.keySet()); @@ -232,26 +238,28 @@ private synchronized int clearOld() { return 0; } int num = 0; - for(K key:keys) { + for (K key : keys) { long time = lastAccessed.get(key); if (time < currentTime - temporaryThreshold) { remove(key); num++; } - } + } return num; } - + private static void clearAllOld() { int num = 0; - for (WeakReference cw : instances) { - Cache c = cw.get(); - if (c != null) { - if (c.temporary) { - num += c.clearOld(); + synchronized (instancesLock) { + for (WeakReference cw : instances) { + Cache c = cw.get(); + if (c != null) { + if (c.temporary) { + num += c.clearOld(); + } } } - } + } if (num > 0) { System.gc(); } diff --git a/resources/com.jpexs.decompiler.flash.metainfo.xml b/resources/com.jpexs.decompiler.flash.metainfo.xml index 5b92bb8509..bc1d62e388 100644 --- a/resources/com.jpexs.decompiler.flash.metainfo.xml +++ b/resources/com.jpexs.decompiler.flash.metainfo.xml @@ -63,6 +63,23 @@ + + +

Fixed

+
    +
  • #1993 Incorrect scroll position causing shapes to be hidden
  • +
  • #1994 Replace command in commandline with three argument causing replacements file load
  • +
  • #1477 Open file (Context menu) with unicode characters, unicode in paths, on Windows
  • +
  • Starting app with parameters causing wrong GUI init
  • +
  • #1991 ConcurrentModificationException on clearing cache thread
  • +
  • #1999 AS3 decompilation - XML constructor call with other than string argument
  • +
+

Changed

+
    +
  • #1996 Items are now exported in order of appearance in the tag tree (usually SWF order), previously was it in order of selection
  • +
+
+

Added

diff --git a/resources/ffdec.exe b/resources/ffdec.exe new file mode 100644 index 0000000000..308cc1cc35 Binary files /dev/null and b/resources/ffdec.exe differ diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index 562f4ab575..c032bf9b5f 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -3143,7 +3143,7 @@ private static void parseReplace(Stack args, String charset, boolean air File inFile = new File(args.pop()); File outFile = new File(args.pop()); - if (args.size() == 3) { + if (args.size() == 1) { System.out.println("Replacing - only single argument passed, taking it as file to load replacements from"); try { List lines = Files.readAllLines(Paths.get(args.pop()), StandardCharsets.UTF_8); diff --git a/src/com/jpexs/decompiler/flash/gui/FolderListPanel.java b/src/com/jpexs/decompiler/flash/gui/FolderListPanel.java index fce2ec0b34..ac075ae4e6 100644 --- a/src/com/jpexs/decompiler/flash/gui/FolderListPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/FolderListPanel.java @@ -33,11 +33,14 @@ import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import java.util.WeakHashMap; import javax.swing.Icon; import javax.swing.JLabel; @@ -63,8 +66,7 @@ public class FolderListPanel extends JPanel { private int selectedIndex = -1; - public Map selectedItems = new HashMap<>(); - + private Map selectedItems = new TreeMap<>(); private static final int PREVIEW_SIZE = 150; @@ -160,7 +162,7 @@ public void mousePressed(MouseEvent e) { } if (SwingUtilities.isRightMouseButton(e)) { - mainPanel.getContextPopupMenu().update(new ArrayList<>(selectedItems.values())); + mainPanel.getContextPopupMenu().update(getSelectedItemsSorted()); mainPanel.getContextPopupMenu().show(FolderListPanel.this, e.getX(), e.getY()); } repaint(); @@ -270,4 +272,12 @@ public void paint(Graphics g) { } } } + + public List getSelectedItemsSorted() { + return new ArrayList<>(selectedItems.values()); + } + + public boolean isSomethingSelected() { + return !selectedItems.isEmpty(); + } } diff --git a/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java index 430e93b241..476cb0154c 100644 --- a/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java @@ -44,16 +44,20 @@ import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.TreeMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; +import javax.swing.tree.TreePath; import org.pushingpixels.substance.api.ColorSchemeAssociationKind; import org.pushingpixels.substance.api.ComponentState; import org.pushingpixels.substance.api.DecorationAreaType; @@ -74,11 +78,7 @@ public class FolderPreviewPanel extends JPanel { private boolean repaintQueued; - private int lastWidth; - - private int lastHeight; - - public Map selectedItems = new LinkedHashMap<>(); + private Map selectedItems = new TreeMap<>(); private Cache cachedPreviews; @@ -156,7 +156,7 @@ public void mousePressed(MouseEvent e) { } if (SwingUtilities.isRightMouseButton(e)) { - mainPanel.getContextPopupMenu().update(new ArrayList<>(selectedItems.values())); + mainPanel.getContextPopupMenu().update(getSelectedItemsSorted()); mainPanel.getContextPopupMenu().show(FolderPreviewPanel.this, e.getX(), e.getY()); } repaint(); @@ -386,4 +386,22 @@ private SerializableImage renderImage(SWF swf, TreeItem treeItem) { } return image; } + + public List getSelectedItemsSorted() { + return new ArrayList<>(selectedItems.values()); + } + + public boolean isSomethingSelected() { + return !selectedItems.isEmpty(); + } + + public Map getSelectedItems() { + return selectedItems; + } + + public void setSelectedItems(Map selectedItems) { + this.selectedItems = selectedItems; + } + + } diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index ad2bae256b..fd6a49a6ad 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -296,14 +296,13 @@ public final class ImagePanel extends JPanel implements MediaDisplay { private JScrollBar verticalScrollBar; private boolean updatingScrollBars = false; private final int SCROLL_SPACE_BEFORE = (int) SWF.unitDivisor * 500; - + private List showPoints1 = new ArrayList<>(); - + private List showPoints2 = new ArrayList<>(); - + private int displayedFrame = 0; - public void setShowPoints(List showPoints1, List showPoints2) { this.showPoints1 = showPoints1; this.showPoints2 = showPoints2; @@ -651,8 +650,9 @@ private void centerImage() { double dw = rect.Xmin * zoomDouble / SWF.unitDivisor; double dh = rect.Ymin * zoomDouble / SWF.unitDivisor; offsetPoint.setLocation( - iconPanel.getWidth() / 2 - w / 2 + dw, - iconPanel.getHeight() / 2 - h / 2 + dh); + iconPanel.getWidth() / 2 - w / 2 - dw, + iconPanel.getHeight() / 2 - h / 2 - dh + ); /*Timer tim = new Timer(); tim.schedule(new TimerTask() { @Override @@ -819,7 +819,7 @@ public void render() { RECT timRect = timelined.getRect(); double zoomDouble = zoom.fit ? getZoomToFit() : zoom.value; AffineTransform trans = new AffineTransform(); - trans.translate(offsetPoint.getX(),offsetPoint.getY()); + trans.translate(offsetPoint.getX(), offsetPoint.getY()); trans.scale(1 / SWF.unitDivisor, 1 / SWF.unitDivisor); trans.scale(zoomDouble, zoomDouble); AffineTransform oldTransform = g2.getTransform(); @@ -931,7 +931,7 @@ public void render() { } }*/ } - + for (int i = 0; i < showPoints1.size(); i++) { int xt = showPoints1.get(i).x; int pointSize = 3; @@ -941,7 +941,7 @@ public void render() { g2.setPaint(Color.blue); g2.fill(pointShape); } - + for (int i = 0; i < showPoints2.size(); i++) { int xt = showPoints2.get(i).x; int pointSize = 3; @@ -1195,14 +1195,14 @@ public void mouseReleased(MouseEvent e) { positions.add(di.pathPoint); splitPositions.add(di.pathPosition); }*/ - for (DistanceItem di:pathPointsUnderCursor) { + for (DistanceItem di : pathPointsUnderCursor) { fireEdgeSplit(di.pathPoint, di.pathPosition); } selectedPoints.clear(); pointsUnderCursor.clear(); pathPointsUnderCursor.clear(); repaint(); - } + } updateScrollBarMinMax(); } @@ -1218,7 +1218,6 @@ public void mouseReleased(MouseEvent e) { registrationPoint = new Point2D.Double(registrationPointUpdated.getX(), registrationPointUpdated.getY()); transform = new Matrix(transformUpdated); transformUpdated = null; - Rectangle2D transBoundsAfter = getTransformBounds(); Point2D transRegPointAfter = registrationPoint; @@ -1236,7 +1235,7 @@ public void mouseReleased(MouseEvent e) { || mode == Cursor.W_RESIZE_CURSOR; if (!isResize && mode != Cursor.DEFAULT_CURSOR && !transRegPointPercentBefore.equals(transRegPointPercentAfter)) { registrationPointPosition = null; - } + } } calcRect(); //do not put this inside synchronized block, it cause deadlock fireBoundsChange(getTransformBounds(), registrationPoint, registrationPointPosition); @@ -1771,7 +1770,7 @@ public void mouseDragged(MouseEvent e) { repaint(); } } - } + } @Override public void mouseMoved(MouseEvent e) { @@ -2062,8 +2061,8 @@ private void calcRect(Zoom z) { } else { w1 = (int) (timelined.getRect().getWidth() * zoomDouble / SWF.unitDivisor); h1 = (int) (timelined.getRect().getHeight() * zoomDouble / SWF.unitDivisor); - dx = (int)(timelined.getRect().Xmin * zoomDouble / SWF.unitDivisor); - dy = (int)(timelined.getRect().Ymin * zoomDouble / SWF.unitDivisor); + dx = (int) (timelined.getRect().Xmin * zoomDouble / SWF.unitDivisor); + dy = (int) (timelined.getRect().Ymin * zoomDouble / SWF.unitDivisor); } //HERE @@ -2217,7 +2216,7 @@ public void hideMouseSelection() { debugLabel.setText(DEFAULT_DEBUG_LABEL_TEXT); } } - + public ImagePanel() { super(new BorderLayout()); //iconPanel.setHorizontalAlignment(JLabel.CENTER); @@ -2286,7 +2285,7 @@ public void adjustmentValueChanged(AdjustmentEvent e) { iconPanel.calcRect(); _viewRect = getViewRect(); updatingScrollBars = false; - redraw(); + redraw(); } }); @@ -2432,8 +2431,6 @@ public synchronized void zoom(Zoom zoom) { public Timelined getTimelined() { return timelined; } - - private void updateScrollBarMinMax() { @@ -2443,13 +2440,13 @@ private void updateScrollBarMinMax() { return; } RECT timRect = timelined.getRect(); - + /* int h_value = horizontalScrollBar.getValue(); int h_visibleAmount = horizontalScrollBar.getVisibleAmount(); */ int h_maximum = timRect.Xmax; - + if (hilightedPoints != null || freeTransformDepth > -1) { h_maximum += SCROLL_SPACE_BEFORE; } @@ -2485,9 +2482,9 @@ private void updateScrollBarMinMax() { verticalScrollBar.setMinimum(v_minimum); verticalScrollBar.setMaximum(v_maximum); - + horizontalScrollBar.setVisible(horizontalScrollBar.getVisibleAmount() < horizontalScrollBar.getMaximum() - horizontalScrollBar.getMinimum()); - verticalScrollBar.setVisible(verticalScrollBar.getVisibleAmount() < verticalScrollBar.getMaximum() - verticalScrollBar.getMinimum()); + verticalScrollBar.setVisible(verticalScrollBar.getVisibleAmount() < verticalScrollBar.getMaximum() - verticalScrollBar.getMinimum()); } private synchronized void updateScrollBars() { @@ -2506,29 +2503,34 @@ public void run() { int w = iconPanel.getWidth(); int h = iconPanel.getHeight(); - int h_visibleAmount = (int) Math.round(w * SWF.unitDivisor / zoomDouble); - + Point2D leftTop = toTransformPoint(new Point2D.Double(0, 0)); - + Point2D rightBottom = toTransformPoint(new Point2D.Double(w, h)); /*if (rightBottom.getX() > timRect.Xmax) { h_visibleAmount = timRect.Xmax - (int)leftTop.getX(); }*/ - - int h_value = (int)Math.round(leftTop.getX());//timRect.Xmin + (int) Math.round(-offsetPoint.getX() / zoomDouble * SWF.unitDivisor); + + int h_value = (int) Math.round(leftTop.getX());//timRect.Xmin + (int) Math.round(-offsetPoint.getX() / zoomDouble * SWF.unitDivisor); horizontalScrollBar.setVisibleAmount(h_visibleAmount); horizontalScrollBar.setValue(h_value); int v_visibleAmount = (int) Math.round(h * SWF.unitDivisor / zoomDouble); - int v_value = (int)Math.round(leftTop.getY()); //timRect.Ymin + (int) Math.round(-offsetPoint.getY() / zoomDouble * SWF.unitDivisor); + int v_value = (int) Math.round(leftTop.getY()); //timRect.Ymin + (int) Math.round(-offsetPoint.getY() / zoomDouble * SWF.unitDivisor); verticalScrollBar.setVisibleAmount(v_visibleAmount); verticalScrollBar.setValue(v_value); updateScrollBarMinMax(); - - /*boolean hVisibleBefore = horizontalScrollBar.isVisible(); + + if (zoom.fit) { + verticalScrollBar.setVisible(false); + horizontalScrollBar.setVisible(false); + updatingScrollBars = false; + return; + } + boolean hVisibleBefore = horizontalScrollBar.isVisible(); horizontalScrollBar.setVisible(horizontalScrollBar.getVisibleAmount() < horizontalScrollBar.getMaximum() - horizontalScrollBar.getMinimum()); boolean hVisibleAfter = horizontalScrollBar.isVisible(); @@ -2536,15 +2538,11 @@ public void run() { verticalScrollBar.setVisible(verticalScrollBar.getVisibleAmount() < verticalScrollBar.getMaximum() - verticalScrollBar.getMinimum()); boolean vVisibleAfter = verticalScrollBar.isVisible(); - if (hVisibleAfter != hVisibleBefore || vVisibleAfter != vVisibleBefore) { updateScrollBars(); - }*/ - updatingScrollBars = false; - if (zoom.fit) { - verticalScrollBar.setVisible(false); - horizontalScrollBar.setVisible(false); } + updatingScrollBars = false; + } }); } @@ -2633,6 +2631,8 @@ public synchronized boolean zoomAvailable() { return zoomAvailable; } + private Timer setTimelinedTimer = null; + public void setTimelined(final Timelined drawable, final SWF swf, int frame, boolean showObjectsUnderCursor, boolean autoPlay, boolean frozen, boolean alwaysDisplay, boolean muted, boolean mutable) { Stage stage = new Stage(drawable) { @Override @@ -2690,7 +2690,8 @@ public void trace(Object... val) { } }; lda = new LocalDataArea(stage); - synchronized (ImagePanel.this) { + synchronized (ImagePanel.this) { + updatingScrollBars = true; stopInternal(); if (drawable instanceof ButtonTag) { frame = ButtonTag.FRAME_UP; @@ -2699,7 +2700,7 @@ public void trace(Object... val) { bounds = null; displayObjectCache.clear(); this.timelined = drawable; - centerImage(); + centerImage(); this.swf = swf; zoomAvailable = true; if (frame > -1) { @@ -2874,7 +2875,7 @@ private synchronized void stopAllSounds() { private void clear() { Timer ptimer = timer; - + if (ptimer != null) { timer = null; ptimer.cancel(); @@ -2886,7 +2887,7 @@ private void clear() { displayObjectCache.clear(); } - private void nextFrame(Timer thisTimer, final int cnt, final int timeShouldBe) { + private void nextFrame(Timer thisTimer, final int cnt, final int timeShouldBe) { synchronized (ImagePanel.class) { if (timelined != null && timer == thisTimer) { int frameCount = timelined.getTimeline().getFrameCount(); @@ -3183,7 +3184,6 @@ private ExportRectangle getViewRect() { viewRect.yMin += timRect.Ymin; viewRect.xMax += timRect.Xmin; viewRect.yMax += timRect.Ymin;*/ - viewRect.xMax = viewRect.xMin + (int) (iconPanel.getWidth() * SWF.unitDivisor / zoomDouble); viewRect.yMax = viewRect.yMin + (int) (iconPanel.getHeight() * SWF.unitDivisor / zoomDouble); return viewRect; @@ -3591,7 +3591,7 @@ public synchronized void play() { frame = 0; prevFrame = -1; } - + startTimer(timeline, true); } } @@ -3652,7 +3652,7 @@ private void scheduleTask(boolean singleFrame, long msDelay, boolean first) { TimerTask task = new TimerTask() { public final Timer thisTimer = timer; - public final boolean isSingleFrame = singleFrame; + public final boolean isSingleFrame = singleFrame; @Override public void run() { @@ -3714,7 +3714,7 @@ public void run() { if (frameCount == 1 || stillFrame) { //We have only one frame, so the ticks on that frame equal ticks on whole timeline currentFrameTicks = ticksFromStart; } - + if (first) { drawFrame(thisTimer, true); } else { @@ -3773,7 +3773,7 @@ public void run() { } timer = new Timer(); fpsShouldBe = timeline.frameRate; - fpsIs = fpsShouldBe; + fpsIs = fpsShouldBe; scheduleTask(singleFrame, 0, true); } @@ -3949,7 +3949,7 @@ private Point2D toImagePoint(Point2D point) { double zoomDouble = zoom.fit ? getZoomToFit() : zoom.value; if (lowQuality) { zoomDouble /= LQ_FACTOR; - } + } double rx = point.getX() * zoomDouble / SWF.unitDivisor + offsetPoint.getX(); // + offsetXRef.getVal(); double ry = point.getY() * zoomDouble / SWF.unitDivisor + offsetPoint.getY(); // + offsetYRef.getVal(); diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index 483530f2a0..aa85624e0b 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -107,6 +107,7 @@ import java.net.Proxy; import java.net.URL; import java.net.URLConnection; +import java.net.URLDecoder; import java.nio.charset.Charset; import java.nio.file.FileSystems; import java.nio.file.StandardWatchEventKinds; @@ -2649,11 +2650,28 @@ public boolean accept(File dir, String name) { } } + /** + * To bypass wrong encoded unicode characters coming from EXE, + * it Launch5j encodes characters using URLEncoder. + * @param args + */ + private static void decodeLaunch5jArgs(String[] args) { + String encargs = System.getProperty("l5j.encargs"); + if ("true".equals(encargs) || "1".equals(encargs)) { + for (int i = 0; i < args.length; ++i) { + try { + args[i] = URLDecoder.decode(args[i], "UTF-8"); + } catch (Exception e) { } + } + } + } + /** * @param args the command line arguments * @throws IOException On error */ public static void main(String[] args) throws IOException { + decodeLaunch5jArgs(args); setSessionLoaded(false); clearTemp(); @@ -2686,13 +2704,13 @@ public static void main(String[] args) throws IOException { reloadLastSession(); } }); - } else { + } else { checkLibraryVersion(); setSessionLoaded(true); String[] filesToOpen = CommandLineArgumentParser.parseArguments(args); if (filesToOpen != null && filesToOpen.length > 0) { + initGui(); View.execInEventDispatch(() -> { - initGui(); shouldCloseWhenClosingLoadingDialog = true; if (Configuration.allowOnlyOneInstance.get() && FirstInstance.openFiles(Arrays.asList(filesToOpen))) { //Try to open in first instance Main.exit(); diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java index e37e9717d4..bdb8aa76d7 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java @@ -1405,18 +1405,7 @@ public void createMenuBar() { path = mainPath + "\\..\\..\\libsrc\\ffdec_lib\\testdata\\as3\\as3.swf"; sourceInfos[1] = new OpenableSourceInfo(null, path, null); Main.openFile(sourceInfos); - }, PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/debug/createNewSwf", "Create new SWF", "continue16", e -> { - SWF swf = new SWF(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - swf.saveTo(baos); - } catch (IOException ex) { - Logger.getLogger(MainFrameMenu.class.getName()).log(Level.SEVERE, null, ex); - } - - Main.openFile(new OpenableSourceInfo(new ByteArrayInputStream(baos.toByteArray()), "new.swf", "New SWF")); - }, PRIORITY_MEDIUM, null, true, null, false); + }, PRIORITY_MEDIUM, null, true, null, false); finishMenu("/debug"); } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 6858804206..f50cf22be9 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -491,12 +491,12 @@ private void handleKeyReleased(KeyEvent e) { Object source = e.getSource(); List items = new ArrayList<>(); if (source == folderPreviewPanel) { - items.addAll(folderPreviewPanel.selectedItems.values()); + items.addAll(folderPreviewPanel.getSelectedItemsSorted()); } else if (source == folderListPanel) { - items.addAll(folderListPanel.selectedItems.values()); + items.addAll(folderListPanel.getSelectedItemsSorted()); } else { AbstractTagTree tree = (AbstractTagTree) e.getSource(); - TreePath[] paths = tree.getSelectionPaths(); + TreePath[] paths = tree.getSelectionPathsSorted(); if (paths != null) { for (TreePath treePath : paths) { TreeItem item = (TreeItem) treePath.getLastPathComponent(); @@ -578,12 +578,12 @@ private void handleKeyPressed(KeyEvent e) { Object source = e.getSource(); List items = new ArrayList<>(); if (source == folderPreviewPanel) { - items.addAll(folderPreviewPanel.selectedItems.values()); + items.addAll(folderPreviewPanel.getSelectedItemsSorted()); } else if (source == folderListPanel) { - items.addAll(folderListPanel.selectedItems.values()); + items.addAll(folderListPanel.getSelectedItemsSorted()); } else { AbstractTagTree tree = (AbstractTagTree) e.getSource(); - TreePath[] paths = tree.getSelectionPaths(); + TreePath[] paths = tree.getSelectionPathsSorted(); if (paths != null) { for (TreePath treePath : paths) { TreeItem item = (TreeItem) treePath.getLastPathComponent(); @@ -5049,7 +5049,7 @@ public void reload(boolean forceReload, boolean scrollToVisible) { JScrollBar folderPreviewScrollBar = ((JScrollPane)folderPreviewPanel.getParent().getParent()).getVerticalScrollBar(); int scrollValue = folderPreviewScrollBar.getValue(); - Map folderItems = new HashMap<>(folderPreviewPanel.selectedItems); + Map folderItems = new HashMap<>(folderPreviewPanel.getSelectedItems()); if (scrollToVisible) { @@ -5269,7 +5269,7 @@ public void reload(boolean forceReload, boolean scrollToVisible) { pinsPanel.setCurrent(oldItem); } - folderPreviewPanel.selectedItems = folderItems; + folderPreviewPanel.setSelectedItems(folderItems); folderPreviewScrollBar.setValue(scrollValue); View.execInEventDispatchLater(new Runnable(){ diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTree.java b/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTree.java index 923bb30578..8340aad6f7 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTree.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/AbstractTagTree.java @@ -102,6 +102,7 @@ import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -501,8 +502,7 @@ public void getAllSubs(TreeItem o, List ret) { } public List getAllSelected() { - TreeSelectionModel tsm = getSelectionModel(); - TreePath[] tps = tsm.getSelectionPaths(); + TreePath[] tps = getSelectionPathsSorted(); List ret = new ArrayList<>(); if (tps == null) { return ret; @@ -525,12 +525,25 @@ public List getAllSubsForItems(List items) { return ret; } + public TreePath[] getSelectionPathsSorted() { + TreePath[] paths = getSelectionPaths(); + if (paths == null) { + return null; + } + Arrays.sort(paths, new Comparator(){ + @Override + public int compare(TreePath o1, TreePath o2) { + return getRowForPath(o1) - getRowForPath(o2); + } + }); + return paths; + } + public List getSelected() { - if (!mainPanel.folderPreviewPanel.selectedItems.isEmpty()) { - return new ArrayList<>(mainPanel.folderPreviewPanel.selectedItems.values()); + if (mainPanel.folderPreviewPanel.isSomethingSelected()) { + return mainPanel.folderPreviewPanel.getSelectedItemsSorted(); } - TreeSelectionModel tsm = getSelectionModel(); - TreePath[] tps = tsm.getSelectionPaths(); + TreePath[] tps = getSelectionPathsSorted(); List ret = new ArrayList<>(); if (tps == null) { return ret; diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java index 1bf76edb43..9545811d88 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java @@ -312,12 +312,14 @@ public static List getSwfFolderItemNestedTagIds(String folderName, bool @Override public List getSelection(Openable openable) { List sel; - if (mainPanel.folderPreviewPanel.selectedItems.isEmpty()) { + if (!mainPanel.folderPreviewPanel.isSomethingSelected()) { sel = getAllSelected(); } else { sel = new ArrayList<>(); - for (TreeItem treeItem : mainPanel.folderPreviewPanel.selectedItems.values()) { + List siSorted = mainPanel.folderPreviewPanel.getSelectedItemsSorted(); + + for (TreeItem treeItem : siSorted) { sel.add(treeItem); getAllSubs(treeItem, sel); } diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index 4d1a4bdb2b..26dda40869 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -645,7 +645,7 @@ public void mouseClicked(MouseEvent e) { getTree().setSelectionRow(row); } - TreePath[] paths = getTree().getSelectionPaths(); + TreePath[] paths = getTree().getSelectionPathsSorted(); if (paths == null || paths.length == 0) { return; } @@ -2775,7 +2775,7 @@ public void tagRemoved(Tag tag) { } }; - if (mainPanel.folderPreviewPanel.selectedItems.isEmpty()) { + if (!mainPanel.folderPreviewPanel.isSomethingSelected()) { mainPanel.treeOperation(r); } else { //current folder must stay selected