mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-07-05 11:55:58 +00:00
swf extracting fixed
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user