swf extracting fixed

This commit is contained in:
Honfika
2014-02-25 21:58:41 +01:00
parent 87884e161e
commit a1f6a335db
9 changed files with 128 additions and 38 deletions

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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<ProgressListener> listeners = new HashSet<>();
private final Map<Long, MemoryInputStream> 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;

View File

@@ -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);
}
}
}

View File

@@ -14,13 +14,13 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.decompiler.flash.console;
package com.jpexs.decompiler.flash;
/**
*
* @author JPEXS
*/
public enum ExtractMode {
public enum SearchMode {
ALL, BIGGEST
}

View File

@@ -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: <infile> 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<Map.Entry<String, SeekableInputStream>> streamsToExtract = new ArrayList<>();
Map.Entry<String, SeekableInputStream> biggest = null;
int biggestSize = 0;
for (Map.Entry<String, SeekableInputStream> 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<String, SeekableInputStream> 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);

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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<TreeNode> 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<Action> 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<ContainerItem> list2 = new ArrayList<>();
list2.addAll(swf.tags);
List<TreeNode> list = createASTagList(list2, null);
TagNode.setExport(list, true);
testAS2DirectEditingOneRecursive(list);
} catch (IOException | InterruptedException ex) {
fail();
}