diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index 2fa58a0ea..40797176c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -84,6 +84,7 @@ import com.jpexs.decompiler.flash.tags.EndTag; import com.jpexs.decompiler.flash.tags.ExportAssetsTag; import com.jpexs.decompiler.flash.tags.FileAttributesTag; import com.jpexs.decompiler.flash.tags.JPEGTablesTag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; import com.jpexs.decompiler.flash.tags.ShowFrameTag; import com.jpexs.decompiler.flash.tags.SymbolClassTag; import com.jpexs.decompiler.flash.tags.Tag; @@ -121,6 +122,7 @@ import com.jpexs.decompiler.flash.treeitems.TreeItem; import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.MATRIX; import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; import com.jpexs.decompiler.flash.types.SHAPE; import com.jpexs.decompiler.flash.types.annotations.Internal; import com.jpexs.decompiler.flash.types.filters.BlendComposite; @@ -3084,4 +3086,29 @@ public final class SWF implements SWFContainerItem, Timelined { } } } + + public static byte[] createEmptySWF(int width, int height) { + SWF s = new SWF(); + s.displayRect = new RECT(0, 0, width * 20, height * 20); + s.compression = SWFCompression.NONE; + s.version = 10; + + s.frameCount = 1; + s.frameRate = 1; + + s.tags.add(new SetBackgroundColorTag(s, new RGB(Color.white))); + s.tags.add(new ShowFrameTag(s)); + s.tags.add(new EndTag(s)); + + s.hasEndTag = true; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + try { + s.saveTo(baos); + } catch (IOException ex) { + Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, null, ex); + } + return baos.toByteArray(); + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java index f2004fbe7..468fd8c82 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -472,6 +472,8 @@ public class Configuration { public static final ConfigurationItem lastSessionFiles = null; + public static final ConfigurationItem lastSessionFileTitles = null; + public static final ConfigurationItem lastSessionSelection = null; @ConfigurationDefaultBoolean(true) diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index 9f564be84..df3c6b821 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.ApplicationInfo; import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFBundle; +import com.jpexs.decompiler.flash.SWFCompression; import com.jpexs.decompiler.flash.SWFSourceInfo; import com.jpexs.decompiler.flash.SearchMode; import com.jpexs.decompiler.flash.SwfOpenException; @@ -35,8 +36,13 @@ import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools; import com.jpexs.decompiler.flash.gui.pipes.FirstInstance; import com.jpexs.decompiler.flash.gui.proxy.ProxyFrame; import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; +import com.jpexs.decompiler.flash.tags.EndTag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; import com.jpexs.decompiler.flash.tags.base.FontTag; import com.jpexs.decompiler.flash.treeitems.SWFList; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; import com.jpexs.helpers.Cache; import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; @@ -49,6 +55,7 @@ import com.sun.jna.platform.win32.Advapi32Util; import com.sun.jna.platform.win32.Kernel32; import com.sun.jna.platform.win32.WinReg; import java.awt.AWTException; +import java.awt.Color; import java.awt.Frame; import java.awt.GraphicsEnvironment; import java.awt.MenuItem; @@ -989,7 +996,15 @@ public class Main { SWF swf = Main.getMainFrame().getPanel().getCurrentSwf(); String title = swf == null ? "?" : swf.getFileTitle(); - openFile(new SWFSourceInfo(new ByteArrayInputStream(data), null, title + ":" + hash)); + title = title + ":" + hash; + String tfile; + try { + tfile = tempFile(title); + Helper.writeFile(tfile, data); + openFile(new SWFSourceInfo(null, tfile, title)); + } catch (IOException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Cannot create tempfile"); + } } @Override @@ -1251,14 +1266,25 @@ public class Main { if (lastSession != null && lastSession.length() > 0) { String[] filesToOpen = lastSession.split(File.pathSeparator, -1); List exfiles = new ArrayList<>(); + List extitles = new ArrayList<>(); + String lastSessionTitles = Configuration.lastSessionFileTitles.get(); + String fileTitles[] = new String[0]; + if (lastSessionTitles != null && !lastSessionTitles.isEmpty()) { + fileTitles = lastSessionTitles.split(File.pathSeparator, -1); + } for (int i = 0; i < filesToOpen.length; i++) { if (new File(filesToOpen[i]).exists()) { exfiles.add(filesToOpen[i]); + if (fileTitles.length > i) { + extitles.add(fileTitles[i]); + } else { + extitles.add(null); + } } } SWFSourceInfo[] sourceInfos = new SWFSourceInfo[exfiles.size()]; for (int i = 0; i < exfiles.size(); i++) { - sourceInfos[i] = new SWFSourceInfo(null, exfiles.get(i), null); + sourceInfos[i] = new SWFSourceInfo(null, exfiles.get(i), extitles.get(i).isEmpty() ? null : extitles.get(i)); } if (sourceInfos.length > 0) { openFile(sourceInfos, () -> { diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameClassic.java b/src/com/jpexs/decompiler/flash/gui/MainFrameClassic.java index 2ad636cdf..ab0dbb300 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainFrameClassic.java +++ b/src/com/jpexs/decompiler/flash/gui/MainFrameClassic.java @@ -16,6 +16,7 @@ */ package com.jpexs.decompiler.flash.gui; +import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; import com.jpexs.helpers.Helper; @@ -28,6 +29,9 @@ import java.awt.event.ComponentEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowStateListener; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.JFrame; /** diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameRibbon.java b/src/com/jpexs/decompiler/flash/gui/MainFrameRibbon.java index c295b2566..76fd86595 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainFrameRibbon.java +++ b/src/com/jpexs/decompiler/flash/gui/MainFrameRibbon.java @@ -16,6 +16,7 @@ */ package com.jpexs.decompiler.flash.gui; +import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; import com.jpexs.decompiler.flash.treeitems.SWFList; @@ -32,7 +33,10 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowStateListener; import java.io.File; +import java.io.IOException; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.JFrame; import org.pushingpixels.flamingo.api.ribbon.JRibbon; import org.pushingpixels.flamingo.internal.ui.ribbon.appmenu.JRibbonApplicationMenuButton; @@ -120,19 +124,25 @@ public final class MainFrameRibbon extends AppRibbonFrame { public void windowClosing(WindowEvent e) { if (Configuration.saveSessionOnExit.get()) { StringBuilder sb = new StringBuilder(); + StringBuilder sbt = new StringBuilder(); + boolean first = true; for (SWFList swf : panel.getSwfs()) { if (!first) { sb.append(File.pathSeparator); + sbt.append(File.pathSeparator); } first = false; String file = swf.sourceInfo.getFile(); if (file != null) { sb.append(file); + String t = swf.sourceInfo.getFileTitle(); + sbt.append(t == null ? "" : t); } } Configuration.lastSessionFiles.set(sb.toString()); + Configuration.lastSessionFileTitles.set(sbt.toString()); String path = panel.tagTree.getSelectionPathString(); if (path != null) { diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties index 2300f7aad..699fd58c7 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties @@ -379,3 +379,7 @@ config.description.enableScriptInitializerDisplay = Enable script initializers d config.name.autoOpenLoadedSWFs = Open loaded SWFs during run (External viewer = WIN only) config.description.autoOpenLoadedSWFs = Opens automatically all SWFs loaded by AS3 class Loader by running SWF when played in FFDec external player. This feature is Windows only. + +config.name.lastSessionFileTitles = Last session file titles +config.description.lastSessionFileTitles = Contains the opened file titles from the last session (for example when loaded from URL etc.) + diff --git a/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java b/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java index f0cd161aa..2610c5c4d 100644 --- a/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java @@ -37,6 +37,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -200,15 +202,19 @@ public final class FlashPlayerPanel extends Panel implements Closeable, MediaDis boolean changed = false; - boolean isPlaying = flash1.IsPlaying(); - if (this.isPlaying != isPlaying) { - this.isPlaying = isPlaying; - } + if (flash1.getReadyState() >= 3) { + boolean isPlaying = flash1.IsPlaying(); + if (this.isPlaying != isPlaying) { + this.isPlaying = isPlaying; + } - int currentFrame = flash1.CurrentFrame(); - if (this.currentFrame != currentFrame) { - this.currentFrame = currentFrame; - changed = true; + int currentFrame = flash1.CurrentFrame(); + if (this.currentFrame != currentFrame) { + this.currentFrame = currentFrame; + changed = true; + } + } else { + this.isPlaying = false; } if (changed) { @@ -249,10 +255,16 @@ public final class FlashPlayerPanel extends Panel implements Closeable, MediaDis if (bgColor != null) { setBackground(bgColor); } + int state = flash.getReadyState(); flash.setMovie(flashName); //play stopped = false; fireMediaDisplayStateChanged(); + try { + Thread.sleep(1000); //Safety delay to avoid access violation. TODO: handle this better. How? + } catch (InterruptedException ex) { + //ignore + } } @Override @@ -263,7 +275,9 @@ public final class FlashPlayerPanel extends Panel implements Closeable, MediaDis @Override public void pause() { try { - flash.Stop(); + if (flash.getReadyState() >= 3) { + flash.Stop(); + } } catch (ActiveXException | NullPointerException ex) { // Can be "Data not available yet exception" } } @@ -271,8 +285,10 @@ public final class FlashPlayerPanel extends Panel implements Closeable, MediaDis @Override public void stop() { try { - flash.Stop(); - flash.Rewind(); + if (flash.getReadyState() >= 3) { + flash.Stop(); + flash.Rewind(); + } } catch (ActiveXException | NullPointerException ex) { // Can be "Data not available yet exception" } } @@ -280,7 +296,9 @@ public final class FlashPlayerPanel extends Panel implements Closeable, MediaDis @Override public void rewind() { try { - flash.Rewind(); + if (flash.getReadyState() >= 3) { + flash.Rewind(); + } } catch (ActiveXException | NullPointerException ex) { // Can be "Data not available yet exception" } } @@ -288,7 +306,9 @@ public final class FlashPlayerPanel extends Panel implements Closeable, MediaDis @Override public void play() { try { - flash.Play(); + if (flash.getReadyState() >= 3) { + flash.Play(); + } } catch (ActiveXException | NullPointerException ex) { // Can be "Data not available yet exception" } } @@ -296,7 +316,11 @@ public final class FlashPlayerPanel extends Panel implements Closeable, MediaDis @Override public boolean isPlaying() { try { - return flash.IsPlaying(); + if (flash.getReadyState() >= 3) { + return flash.IsPlaying(); + } else { + return false; + } } catch (ActiveXException | NullPointerException ex) { // Can be "Data not available yet exception" return false; } @@ -315,7 +339,9 @@ public final class FlashPlayerPanel extends Panel implements Closeable, MediaDis return; } try { - flash.GotoFrame(frame); + if (flash.getReadyState() >= 3) { + flash.GotoFrame(frame); + } } catch (ActiveXException | NullPointerException ex) { // Can be "Data not available yet exception" } }