diff --git a/trunk/src/com/jpexs/decompiler/flash/BinarySWFBundle.java b/trunk/src/com/jpexs/decompiler/flash/BinarySWFBundle.java index 87d5693d6..fcb48b052 100644 --- a/trunk/src/com/jpexs/decompiler/flash/BinarySWFBundle.java +++ b/trunk/src/com/jpexs/decompiler/flash/BinarySWFBundle.java @@ -33,8 +33,8 @@ public class BinarySWFBundle implements SWFBundle { private final SWFSearch search; - public BinarySWFBundle(InputStream is, boolean noCheck) { - search = new SWFSearch(new StreamSearch(is), noCheck); + public BinarySWFBundle(InputStream is, boolean noCheck, SearchMode searchMode) { + search = new SWFSearch(new StreamSearch(is), noCheck, searchMode); search.process(); } diff --git a/trunk/src/com/jpexs/decompiler/flash/SWF.java b/trunk/src/com/jpexs/decompiler/flash/SWF.java index ccda3d9d8..01e5abba9 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWF.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWF.java @@ -413,6 +413,10 @@ public final class SWF implements TreeItem { frameRate = sis.readUI8(); frameCount = sis.readUI16(); if (skipTagReading) { + long toRead = fileSize - sis.getPos(); + if (toRead > 0) { + sis.readBytes(toRead); + } return; } tags = sis.readTagList(this, 0, parallelRead, true, !checkOnly, gfx); diff --git a/trunk/src/com/jpexs/decompiler/flash/SWFSearch.java b/trunk/src/com/jpexs/decompiler/flash/SWFSearch.java index ffb08c3cf..1f1752f39 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWFSearch.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWFSearch.java @@ -35,13 +35,15 @@ public class SWFSearch { protected Searchable s; private final boolean noCheck; + private final SearchMode searchMode; private boolean processed = false; private final Set listeners = new HashSet<>(); private final Map swfStreams = new HashMap<>(); - public SWFSearch(Searchable s, boolean noCheck) { + public SWFSearch(Searchable s, boolean noCheck, SearchMode searchMode) { this.s = s; this.noCheck = noCheck; + this.searchMode = searchMode; } public void addProgressListener(ProgressListener l) { @@ -72,6 +74,7 @@ public class SWFSearch { "GFX".getBytes(), //Uncompressed ScaleForm GFx "CFX".getBytes()); //Compressed ScaleForm GFx int pos = 0; + long biggestSize = 0; for (Long addr : ret.keySet()) { setProgress(pos * 100 / ret.size()); pos++; @@ -80,17 +83,29 @@ public class SWFSearch { mis.reset(); PosMarkedInputStream pmi = new PosMarkedInputStream(mis); SWF swf = new SWF(pmi, null, false, true, noCheck); - long limit = pmi.getPos(); - MemoryInputStream is = new MemoryInputStream(mis.getAllRead(), (int) (long) addr, (int) limit); - if (swf.fileSize > 0 && swf.version > 0 && !swf.tags.isEmpty() && swf.version < 25/*Needs to be fixed when SWF versions reaches this value*/) { - swfStreams.put(addr, is); + boolean valid = swf.fileSize > 0 + && swf.version > 0 + && (!swf.tags.isEmpty() || noCheck) + && swf.version < 25; // Needs to be fixed when SWF versions reaches this value + if (valid) { + long limit = pmi.getPos(); + MemoryInputStream is = new MemoryInputStream(mis.getAllRead(), (int) (long) addr, (int) limit); + switch (searchMode) { + case ALL: + swfStreams.put(addr, is); + break; + case BIGGEST: + if (limit > biggestSize) { + biggestSize = limit; + swfStreams.clear(); + swfStreams.put(addr, is); + } + } } - } catch (OutOfMemoryError ome) { System.gc(); } catch (Exception | Error ex) { } - } setProgress(100); processed = true; diff --git a/trunk/src/com/jpexs/decompiler/flash/SWFSourceInfo.java b/trunk/src/com/jpexs/decompiler/flash/SWFSourceInfo.java index 3c03a0dca..0c88f7924 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWFSourceInfo.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWFSourceInfo.java @@ -76,7 +76,7 @@ public class SWFSourceInfo { return false; } - public SWFBundle getBundle(boolean noCheck) throws IOException { + public SWFBundle getBundle(boolean noCheck, SearchMode searchMode) throws IOException { if (!isBundle()) { return null; } @@ -89,7 +89,7 @@ public class SWFSourceInfo { case ".zip": return new ZippedSWFBundle(is); default: - return new BinarySWFBundle(is, noCheck); + return new BinarySWFBundle(is, noCheck, searchMode); } } } diff --git a/trunk/src/com/jpexs/decompiler/flash/console/ExtractMode.java b/trunk/src/com/jpexs/decompiler/flash/SearchMode.java similarity index 88% rename from trunk/src/com/jpexs/decompiler/flash/console/ExtractMode.java rename to trunk/src/com/jpexs/decompiler/flash/SearchMode.java index ac7bf4143..eb0351a4d 100644 --- a/trunk/src/com/jpexs/decompiler/flash/console/ExtractMode.java +++ b/trunk/src/com/jpexs/decompiler/flash/SearchMode.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.jpexs.decompiler.flash.console; +package com.jpexs.decompiler.flash; /** * * @author JPEXS */ -public enum ExtractMode { - +public enum SearchMode { + ALL, BIGGEST } diff --git a/trunk/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/trunk/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index 430ab426f..c0cdfdc2e 100644 --- a/trunk/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/trunk/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFBundle; import com.jpexs.decompiler.flash.SWFSourceInfo; +import com.jpexs.decompiler.flash.SearchMode; import com.jpexs.decompiler.flash.abc.RenameType; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.configuration.ConfigurationItem; @@ -754,7 +755,7 @@ public class CommandLineArgumentParser { } String fileName = args.remove(); - ExtractMode mode = ExtractMode.ALL; + SearchMode mode = SearchMode.ALL; boolean noCheck = false; if (args.size() > 0) { @@ -768,7 +769,7 @@ public class CommandLineArgumentParser { String modeStr = args.remove().toLowerCase(); switch (modeStr) { case "biggest": - mode = ExtractMode.BIGGEST; + mode = SearchMode.BIGGEST; break; } } @@ -779,36 +780,25 @@ public class CommandLineArgumentParser { System.err.println("Error: should be a bundle. (ZIP or non SWF binary file)"); System.exit(1); } - SWFBundle bundle = sourceInfo.getBundle(noCheck); + SWFBundle bundle = sourceInfo.getBundle(noCheck, mode); List> streamsToExtract = new ArrayList<>(); - Map.Entry biggest = null; - int biggestSize = 0; for (Map.Entry streamEntry : bundle.getAll().entrySet()) { InputStream stream = streamEntry.getValue(); stream.reset(); - switch (mode) { - case ALL: - streamsToExtract.add(streamEntry); - break; - case BIGGEST: - byte[] swfData = new byte[stream.available()]; - int available = stream.read(swfData); // stream.available() reports wrong value - if (available > biggestSize) { - biggest = streamEntry; - biggestSize = available; - } - break; - } - } - - if (mode == ExtractMode.BIGGEST && biggest != null) { - streamsToExtract.add(biggest); + streamsToExtract.add(streamEntry); } for (Map.Entry streamEntry : streamsToExtract) { InputStream stream = streamEntry.getValue(); stream.reset(); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(streamEntry.getKey() + ".swf"))) { + String fileNameOut; + if (mode == SearchMode.BIGGEST) { + fileNameOut = Helper.getWithoutExtension(new File(fileName)) + ".swf"; + } else { + fileNameOut = streamEntry.getKey() + ".swf"; + } + + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(fileNameOut))) { byte[] swfData = new byte[stream.available()]; int cnt = stream.read(swfData); fos.write(swfData, 0, cnt); diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/Main.java b/trunk/src/com/jpexs/decompiler/flash/gui/Main.java index 4aeb2706c..58df2166b 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/Main.java @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFBundle; import com.jpexs.decompiler.flash.SWFSourceInfo; +import com.jpexs.decompiler.flash.SearchMode; import com.jpexs.decompiler.flash.Version; import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; import com.jpexs.decompiler.flash.configuration.Configuration; @@ -230,7 +231,7 @@ public class Main { SWFBundle bundle = null; if (inputStream == null) { inputStream = new BufferedInputStream(new FileInputStream(sourceInfo.getFile())); - bundle = sourceInfo.getBundle(false); + bundle = sourceInfo.getBundle(false, SearchMode.ALL); logger.log(Level.INFO, "Load file: {0}", sourceInfo.getFile()); } else if (inputStream instanceof SeekableInputStream || inputStream instanceof BufferedInputStream) { diff --git a/trunk/src/com/jpexs/helpers/Helper.java b/trunk/src/com/jpexs/helpers/Helper.java index 657a8b63c..06e3a4b42 100644 --- a/trunk/src/com/jpexs/helpers/Helper.java +++ b/trunk/src/com/jpexs/helpers/Helper.java @@ -772,6 +772,17 @@ public class Helper { return ext; } + public static String getWithoutExtension(File f) { + String ext = null; + String s = f.getName(); + int i = s.lastIndexOf('.'); + + if (i > 0 && i < s.length() - 1) { + ext = s.substring(0, i).toLowerCase(); + } + return ext; + } + public static void appendTimeoutComment(GraphTextWriter writer, int timeout) { writer.appendNoHilight("/*").newLine(); writer.appendNoHilight(" * ").appendNoHilight(AppStrings.translate("decompilationError")).newLine(); diff --git a/trunk/test/com/jpexs/decompiler/flash/RecompileTest.java b/trunk/test/com/jpexs/decompiler/flash/RecompileTest.java index 86621b931..df1069f93 100644 --- a/trunk/test/com/jpexs/decompiler/flash/RecompileTest.java +++ b/trunk/test/com/jpexs/decompiler/flash/RecompileTest.java @@ -16,14 +16,31 @@ */ package com.jpexs.decompiler.flash; +import static com.jpexs.decompiler.flash.SWF.createASTagList; import com.jpexs.decompiler.flash.abc.NotSameException; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.parser.ParseException; +import com.jpexs.decompiler.flash.action.parser.script.ActionScriptParser; import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.helpers.CodeFormatting; +import com.jpexs.decompiler.flash.helpers.HilightedTextWriter; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.tags.base.ContainerItem; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.flash.treenodes.TagNode; +import com.jpexs.decompiler.flash.treenodes.TreeNode; +import com.jpexs.decompiler.graph.ExportMode; +import com.jpexs.decompiler.graph.TranslateException; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FilenameFilter; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import static org.testng.Assert.fail; import org.testng.annotations.Test; @@ -64,9 +81,61 @@ public class RecompileTest { } } + private void testAS2DirectEditingOneRecursive(List nodeList) { + for (TreeNode node : nodeList) { + if (node.subNodes.isEmpty()) { + TreeItem item = node.getItem(); + if ((item instanceof ASMSource) && (node.export)) { + boolean retry; + do { + retry = false; + try { + ASMSource asm = ((ASMSource) item); + HilightedTextWriter writer = new HilightedTextWriter(new CodeFormatting(), false); + asm.getActionSourcePrefix(writer); + asm.getASMSource(ExportMode.SOURCE, writer, null); + asm.getActionSourceSuffix(writer); + String as = writer.toString(); + ActionScriptParser par = new ActionScriptParser(); + List actions = null; + try { + actions = par.actionsFromString(as); + } catch (ParseException ex) { + fail("Unable to parse: " + item.getSwf().getShortFileName() + "/" + item.toString()); + } + writer = new HilightedTextWriter(new CodeFormatting(), false); + Action.actionsToSource(asm, actions, asm.toString()/*FIXME?*/, writer); + String as2 = writer.toString(); + try { + actions = par.actionsFromString(as2); + } catch (ParseException ex) { + fail("Unable to parse: " + item.getSwf().getShortFileName() + "/" + item.toString()); + } + writer = new HilightedTextWriter(new CodeFormatting(), false); + Action.actionsToSource(asm, actions, asm.toString()/*FIXME?*/, writer); + String as3 = writer.toString(); + if (!as3.equals(as2)) { + fail("ActionScript is diffrent: " + item.getSwf().getShortFileName() + "/" + item.toString()); + } + } catch (InterruptedException | IOException | OutOfMemoryError | TranslateException | StackOverflowError ex) { + } + } while (retry); + } + } else { + testAS2DirectEditingOneRecursive(node.subNodes); + } + } + } + private void testAS2DirectEditingOne(String filename) { try { SWF swf = new SWF(new BufferedInputStream(new FileInputStream(TESTDATADIR + File.separator + filename)), false); + List list2 = new ArrayList<>(); + list2.addAll(swf.tags); + List list = createASTagList(list2, null); + + TagNode.setExport(list, true); + testAS2DirectEditingOneRecursive(list); } catch (IOException | InterruptedException ex) { fail(); }