mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-07-13 21:29:55 +00:00
Issue #431: Canceling background threads (after timeout, or after user action, e.g new treeitem selected) fixed + small fixes
This commit is contained in:
@@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.abc.ClassPath;
|
||||
import com.jpexs.decompiler.flash.abc.RenameType;
|
||||
import com.jpexs.decompiler.flash.abc.ScriptPack;
|
||||
import com.jpexs.decompiler.flash.action.Action;
|
||||
import com.jpexs.decompiler.flash.action.ActionDeobfuscation;
|
||||
import com.jpexs.decompiler.flash.action.ActionGraphSource;
|
||||
import com.jpexs.decompiler.flash.action.model.ConstantPool;
|
||||
import com.jpexs.decompiler.flash.action.model.DirectValueActionItem;
|
||||
@@ -101,6 +102,7 @@ import com.jpexs.decompiler.graph.GraphSourceItemContainer;
|
||||
import com.jpexs.decompiler.graph.GraphTargetItem;
|
||||
import com.jpexs.decompiler.graph.model.LocalData;
|
||||
import com.jpexs.helpers.Cache;
|
||||
import com.jpexs.helpers.CancellableWorker;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import com.jpexs.helpers.ProgressListener;
|
||||
import com.jpexs.helpers.utf8.Utf8Helper;
|
||||
@@ -129,7 +131,6 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.Callable;
|
||||
@@ -142,7 +143,6 @@ import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
import javax.imageio.ImageIO;
|
||||
@@ -298,7 +298,7 @@ public final class SWF {
|
||||
}
|
||||
}
|
||||
|
||||
public SWF(InputStream is, boolean parallelRead) throws IOException {
|
||||
public SWF(InputStream is, boolean parallelRead) throws IOException, InterruptedException {
|
||||
this(is, null, parallelRead);
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ public final class SWF {
|
||||
* @param parallelRead Use parallel threads?
|
||||
* @throws IOException
|
||||
*/
|
||||
public SWF(InputStream is, ProgressListener listener, boolean parallelRead) throws IOException {
|
||||
public SWF(InputStream is, ProgressListener listener, boolean parallelRead) throws IOException, InterruptedException {
|
||||
this(is, listener, parallelRead, false);
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ public final class SWF {
|
||||
* @param checkOnly Check only file validity
|
||||
* @throws IOException
|
||||
*/
|
||||
public SWF(InputStream is, ProgressListener listener, boolean parallelRead, boolean checkOnly) throws IOException {
|
||||
public SWF(InputStream is, ProgressListener listener, boolean parallelRead, boolean checkOnly) throws IOException, InterruptedException {
|
||||
byte[] hdr = new byte[3];
|
||||
is.read(hdr);
|
||||
String shdr = new String(hdr, Utf8Helper.charset);
|
||||
@@ -663,7 +663,7 @@ public final class SWF {
|
||||
long time = stopTime - startTime;
|
||||
informListeners("exported", "Exported script " + currentIndex + "/" + count + " " + path + ", " + Helper.formatTimeSec(time));
|
||||
}
|
||||
return null;
|
||||
return rio.result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -696,7 +696,7 @@ public final class SWF {
|
||||
|
||||
if (!parallel || packs.size() < 2) {
|
||||
try {
|
||||
Helper.timedCall(new Callable<Void>() {
|
||||
CancellableWorker.call(new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
for (MyEntry<ClassPath, ScriptPack> item : packs) {
|
||||
@@ -712,26 +712,32 @@ public final class SWF {
|
||||
Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, "Error during ABC export", ex);
|
||||
}
|
||||
} else {
|
||||
ExecutorService executor = Executors.newFixedThreadPool(20);
|
||||
ExecutorService executor = Executors.newFixedThreadPool(Configuration.parallelThreadCount.get());
|
||||
List<Future<File>> futureResults = new ArrayList<>();
|
||||
for (MyEntry<ClassPath, ScriptPack> item : packs) {
|
||||
Future<File> future = executor.submit(new ExportPackTask(handler, cnt, packs.size(), item.key, item.value, outdir, abcTags, exportMode, parallel));
|
||||
futureResults.add(future);
|
||||
}
|
||||
|
||||
for (int f = 0; f < futureResults.size(); f++) {
|
||||
try {
|
||||
ret.add(futureResults.get(f).get());
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "Error during ABC export", ex);
|
||||
try {
|
||||
executor.shutdown();
|
||||
if (!executor.awaitTermination(Configuration.exportTimeout.get(), TimeUnit.SECONDS)) {
|
||||
Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, Helper.formatTimeToText(Configuration.exportTimeout.get()) + " ActionScript export limit reached");
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
} finally {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
|
||||
try {
|
||||
executor.awaitTermination(Configuration.exportTimeout.get(), TimeUnit.SECONDS);
|
||||
executor.shutdownNow();
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, Helper.formatTimeToText(Configuration.exportTimeout.get()) + " ActionScript export limit reached", ex);
|
||||
for (int f = 0; f < futureResults.size(); f++) {
|
||||
try {
|
||||
if (futureResults.get(f).isDone()) {
|
||||
ret.add(futureResults.get(f).get());
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
} catch (ExecutionException ex) {
|
||||
Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "Error during ABC export", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1482,133 +1488,12 @@ public final class SWF {
|
||||
public void exportBinaryData(AbortRetryIgnoreHandler handler, String outdir) throws IOException {
|
||||
exportBinaryData(handler, outdir, tags);
|
||||
}
|
||||
public static final String[] reservedWords = {
|
||||
"as", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "each", "else",
|
||||
"extends", "false", "finally", "for", "function", "get", "if", "implements", "import", "in", "instanceof",
|
||||
"interface", "internal", "is", "native", "new", "null", "override", "package", "private", "protected", "public",
|
||||
"return", "set", "super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while",
|
||||
"with", "dynamic", "default", "final", "in"};
|
||||
|
||||
private boolean isReserved(String s) {
|
||||
for (String rw : reservedWords) {
|
||||
if (rw.equals(s.trim())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private HashMap<String, String> deobfuscated = new HashMap<>();
|
||||
private Random rnd = new Random();
|
||||
private final int DEFAULT_FOO_SIZE = 10;
|
||||
public static final String validFirstCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
|
||||
public static final String validNextCharacters = validFirstCharacters + "0123456789";
|
||||
public static final String fooCharacters = "bcdfghjklmnpqrstvwz";
|
||||
public static final String fooJoinCharacters = "aeiouy";
|
||||
private List<MyEntry<DirectValueActionItem, ConstantPool>> allVariableNames = new ArrayList<>();
|
||||
private HashSet<String> allVariableNamesStr = new HashSet<>();
|
||||
private List<GraphSourceItem> allFunctions = new ArrayList<>();
|
||||
private HashMap<DirectValueActionItem, ConstantPool> allStrings = new HashMap<>();
|
||||
private HashMap<DirectValueActionItem, String> usageTypes = new HashMap<>();
|
||||
|
||||
private String fooString(String orig, boolean firstUppercase, int rndSize) {
|
||||
boolean exists;
|
||||
String ret;
|
||||
loopfoo:
|
||||
do {
|
||||
exists = false;
|
||||
int len = 3 + rnd.nextInt(rndSize - 3);
|
||||
ret = "";
|
||||
for (int i = 0; i < len; i++) {
|
||||
String c = "";
|
||||
if ((i % 2) == 0) {
|
||||
c = "" + fooCharacters.charAt(rnd.nextInt(fooCharacters.length()));
|
||||
} else {
|
||||
c = "" + fooJoinCharacters.charAt(rnd.nextInt(fooJoinCharacters.length()));
|
||||
}
|
||||
if (i == 0 && firstUppercase) {
|
||||
c = c.toUpperCase(Locale.ENGLISH);
|
||||
}
|
||||
ret += c;
|
||||
}
|
||||
if (allVariableNamesStr.contains(ret)) {
|
||||
exists = true;
|
||||
rndSize += 1;
|
||||
continue loopfoo;
|
||||
}
|
||||
if (isReserved(ret)) {
|
||||
exists = true;
|
||||
rndSize += 1;
|
||||
continue;
|
||||
}
|
||||
if (deobfuscated.containsValue(ret)) {
|
||||
exists = true;
|
||||
rndSize += 1;
|
||||
continue;
|
||||
}
|
||||
} while (exists);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public String deobfuscateName(String s, boolean firstUppercase, String usageType, RenameType renameType, Map<String, String> selected) {
|
||||
boolean isValid = true;
|
||||
if (usageType == null) {
|
||||
usageType = "name";
|
||||
}
|
||||
|
||||
if (selected != null) {
|
||||
if (selected.containsKey(s)) {
|
||||
return selected.get(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (isReserved(s)) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (s.charAt(i) > 127) {
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
Pattern pat = Pattern.compile("^[" + Pattern.quote(validFirstCharacters) + "]" + "[" + Pattern.quote(validFirstCharacters + validNextCharacters) + "]*$");
|
||||
if (!pat.matcher(s).matches()) {
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
if (!isValid) {
|
||||
if (deobfuscated.containsKey(s)) {
|
||||
return deobfuscated.get(s);
|
||||
} else {
|
||||
Integer cnt = typeCounts.get(usageType);
|
||||
if (cnt == null) {
|
||||
cnt = 0;
|
||||
}
|
||||
|
||||
String ret = null;
|
||||
if (renameType == RenameType.TYPENUMBER) {
|
||||
|
||||
boolean found;
|
||||
do {
|
||||
found = false;
|
||||
cnt++;
|
||||
ret = usageType + "_" + cnt;
|
||||
found = allVariableNamesStr.contains(ret);
|
||||
} while (found);
|
||||
typeCounts.put(usageType, cnt);
|
||||
} else if (renameType == RenameType.RANDOMWORD) {
|
||||
ret = fooString(s, firstUppercase, DEFAULT_FOO_SIZE);
|
||||
}
|
||||
deobfuscated.put(s, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private ActionDeobfuscation deobfuscation = new ActionDeobfuscation();
|
||||
|
||||
private static void getVariables(ConstantPool constantPool, List<Object> localData, Stack<GraphTargetItem> stack, List<GraphTargetItem> output, ActionGraphSource code, int ip, List<MyEntry<DirectValueActionItem, ConstantPool>> variables, List<GraphSourceItem> functions, HashMap<DirectValueActionItem, ConstantPool> strings, List<Integer> visited, HashMap<DirectValueActionItem, String> usageTypes, String path) throws InterruptedException {
|
||||
boolean debugMode = false;
|
||||
@@ -1638,6 +1523,9 @@ public final class SWF {
|
||||
|| (ins instanceof ActionNewObject)
|
||||
|| (ins instanceof ActionCallMethod)
|
||||
|| (ins instanceof ActionCallFunction)) {
|
||||
if (stack.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
name = stack.peek();
|
||||
}
|
||||
|
||||
@@ -1681,12 +1569,18 @@ public final class SWF {
|
||||
r.add(new ArrayList<GraphTargetItem>());
|
||||
r.add(new ArrayList<GraphTargetItem>());
|
||||
r.add(new ArrayList<GraphTargetItem>());
|
||||
((GraphSourceItemContainer) ins).translateContainer(r, stack, output, new HashMap<Integer, String>(), new HashMap<String, GraphTargetItem>(), new HashMap<String, GraphTargetItem>());
|
||||
try {
|
||||
((GraphSourceItemContainer) ins).translateContainer(r, stack, output, new HashMap<Integer, String>(), new HashMap<String, GraphTargetItem>(), new HashMap<String, GraphTargetItem>());
|
||||
} catch (EmptyStackException ex) {
|
||||
}
|
||||
//ip++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((ins instanceof ActionSetVariable) || (ins instanceof ActionSetMember) || (ins instanceof ActionDefineLocal)) {
|
||||
if (stack.size() < 2) {
|
||||
break;
|
||||
}
|
||||
name = stack.get(stack.size() - 2);
|
||||
}
|
||||
|
||||
@@ -1718,8 +1612,6 @@ public final class SWF {
|
||||
} catch (EmptyStackException ex) {
|
||||
// probably obfucated code, never executed branch
|
||||
break;
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "Error during getting variables", ex);
|
||||
}
|
||||
if (ins.isExit()) {
|
||||
break;
|
||||
@@ -1742,6 +1634,9 @@ public final class SWF {
|
||||
|
||||
if (ins.isBranch() || ins.isJump()) {
|
||||
if (ins instanceof ActionIf) {
|
||||
if (stack.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
stack.pop();
|
||||
}
|
||||
visited.add(ip);
|
||||
@@ -1799,96 +1694,6 @@ public final class SWF {
|
||||
}
|
||||
}
|
||||
|
||||
public void deobfuscateInstanceNames(RenameType renameType, List<Tag> tags, Map<String, String> selected) {
|
||||
for (Tag t : tags) {
|
||||
if (t instanceof DefineSpriteTag) {
|
||||
deobfuscateInstanceNames(renameType, ((DefineSpriteTag) t).subTags, selected);
|
||||
}
|
||||
if (t instanceof PlaceObjectTypeTag) {
|
||||
PlaceObjectTypeTag po = (PlaceObjectTypeTag) t;
|
||||
String name = po.getInstanceName();
|
||||
if (name != null) {
|
||||
String changedName = deobfuscateName(name, false, "instance", renameType, selected);
|
||||
if (changedName != null) {
|
||||
po.setInstanceName(changedName);
|
||||
}
|
||||
}
|
||||
String className = po.getClassName();
|
||||
if (className != null) {
|
||||
String changedClassName = deobfuscateNameWithPackage(className, renameType, selected);
|
||||
if (changedClassName != null) {
|
||||
po.setClassName(changedClassName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String deobfuscatePackage(String pkg, RenameType renameType, Map<String, String> selected) {
|
||||
if (deobfuscated.containsKey(pkg)) {
|
||||
return deobfuscated.get(pkg);
|
||||
}
|
||||
String[] parts = null;
|
||||
if (pkg.contains(".")) {
|
||||
parts = pkg.split("\\.");
|
||||
} else {
|
||||
parts = new String[]{pkg};
|
||||
}
|
||||
String ret = "";
|
||||
boolean isChanged = false;
|
||||
for (int p = 0; p < parts.length; p++) {
|
||||
if (p > 0) {
|
||||
ret += ".";
|
||||
}
|
||||
String partChanged = deobfuscateName(parts[p], false, "package", renameType, selected);
|
||||
if (partChanged != null) {
|
||||
ret += partChanged;
|
||||
isChanged = true;
|
||||
} else {
|
||||
ret += parts[p];
|
||||
}
|
||||
}
|
||||
if (isChanged) {
|
||||
deobfuscated.put(pkg, ret);
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String deobfuscateNameWithPackage(String n, RenameType renameType, Map<String, String> selected) {
|
||||
String pkg = null;
|
||||
String name = "";
|
||||
if (n.contains(".")) {
|
||||
pkg = n.substring(0, n.lastIndexOf('.'));
|
||||
name = n.substring(n.lastIndexOf('.') + 1);
|
||||
} else {
|
||||
name = n;
|
||||
}
|
||||
boolean changed = false;
|
||||
if ((pkg != null) && (!pkg.isEmpty())) {
|
||||
String changedPkg = deobfuscatePackage(pkg, renameType, selected);
|
||||
if (changedPkg != null) {
|
||||
changed = true;
|
||||
pkg = changedPkg;
|
||||
}
|
||||
}
|
||||
String changedName = deobfuscateName(name, true, "class", renameType, selected);
|
||||
if (changedName != null) {
|
||||
changed = true;
|
||||
name = changedName;
|
||||
}
|
||||
if (changed) {
|
||||
String newClassName = "";
|
||||
if (pkg == null) {
|
||||
newClassName = name;
|
||||
} else {
|
||||
newClassName = pkg + "." + name;
|
||||
}
|
||||
return newClassName;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int deobfuscateAS3Identifiers(RenameType renameType) {
|
||||
for (Tag tag : tags) {
|
||||
if (tag instanceof ABCContainerTag) {
|
||||
@@ -1904,17 +1709,16 @@ public final class SWF {
|
||||
if (tag instanceof SymbolClassTag) {
|
||||
SymbolClassTag sc = (SymbolClassTag) tag;
|
||||
for (int i = 0; i < sc.classNames.length; i++) {
|
||||
String newname = deobfuscateNameWithPackage(sc.classNames[i], renameType, deobfuscated);
|
||||
String newname = deobfuscation.deobfuscateNameWithPackage(sc.classNames[i], deobfuscated, renameType, deobfuscated);
|
||||
if (newname != null) {
|
||||
sc.classNames[i] = newname;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
deobfuscateInstanceNames(renameType, tags, new HashMap<String, String>());
|
||||
deobfuscation.deobfuscateInstanceNames(deobfuscated, renameType, tags, new HashMap<String, String>());
|
||||
return deobfuscated.size();
|
||||
}
|
||||
HashMap<String, Integer> typeCounts = new HashMap<>();
|
||||
|
||||
public int deobfuscateIdentifiers(RenameType renameType) throws InterruptedException {
|
||||
findFileAttributes();
|
||||
@@ -1956,7 +1760,7 @@ public final class SWF {
|
||||
int fc = 0;
|
||||
for (MyEntry<DirectValueActionItem, ConstantPool> it : allVariableNames) {
|
||||
String name = it.key.toStringNoH(it.value);
|
||||
allVariableNamesStr.add(name);
|
||||
deobfuscation.allVariableNamesStr.add(name);
|
||||
}
|
||||
|
||||
informListeners("rename", "classes");
|
||||
@@ -1984,7 +1788,12 @@ public final class SWF {
|
||||
}
|
||||
}
|
||||
int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC;
|
||||
List<GraphTargetItem> dec = Action.actionsToTree(dia.getActions(version), version, staticOperation, ""/*FIXME*/);
|
||||
List<GraphTargetItem> dec;
|
||||
try {
|
||||
dec = Action.actionsToTree(dia.getActions(version), version, staticOperation, ""/*FIXME*/);
|
||||
} catch (EmptyStackException ex) {
|
||||
continue;
|
||||
}
|
||||
GraphTargetItem name = null;
|
||||
for (GraphTargetItem it : dec) {
|
||||
if (it instanceof ClassActionItem) {
|
||||
@@ -1999,7 +1808,7 @@ public final class SWF {
|
||||
if (fun.calculatedFunctionName instanceof DirectValueActionItem) {
|
||||
DirectValueActionItem dvf = (DirectValueActionItem) fun.calculatedFunctionName;
|
||||
String fname = dvf.toStringNoH(null);
|
||||
String changed = deobfuscateName(fname, false, "method", renameType, selected);
|
||||
String changed = deobfuscation.deobfuscateName(fname, false, "method", deobfuscated, renameType, selected);
|
||||
if (changed != null) {
|
||||
deobfuscated.put(fname, changed);
|
||||
}
|
||||
@@ -2019,7 +1828,7 @@ public final class SWF {
|
||||
if (gti instanceof DirectValueActionItem) {
|
||||
DirectValueActionItem dvf = (DirectValueActionItem) gti;
|
||||
String vname = dvf.toStringNoH(null);
|
||||
String changed = deobfuscateName(vname, false, "attribute", renameType, selected);
|
||||
String changed = deobfuscation.deobfuscateName(vname, false, "attribute", deobfuscated, renameType, selected);
|
||||
if (changed != null) {
|
||||
deobfuscated.put(vname, changed);
|
||||
}
|
||||
@@ -2053,7 +1862,7 @@ public final class SWF {
|
||||
if (classNameParts != null) {
|
||||
changedNameStr = classNameParts[classNameParts.length - 1 - pos];
|
||||
}
|
||||
String changedNameStr2 = deobfuscateName(changedNameStr, pos == 0, pos == 0 ? "class" : "package", renameType, selected);
|
||||
String changedNameStr2 = deobfuscation.deobfuscateName(changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected);
|
||||
if (changedNameStr2 != null) {
|
||||
changedNameStr = changedNameStr2;
|
||||
}
|
||||
@@ -2077,7 +1886,7 @@ public final class SWF {
|
||||
if (classNameParts != null) {
|
||||
changedNameStr = classNameParts[classNameParts.length - 1 - pos];
|
||||
}
|
||||
String changedNameStr2 = deobfuscateName(changedNameStr, pos == 0, pos == 0 ? "class" : "package", renameType, selected);
|
||||
String changedNameStr2 = deobfuscation.deobfuscateName(changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected);
|
||||
if (changedNameStr2 != null) {
|
||||
changedNameStr = changedNameStr2;
|
||||
}
|
||||
@@ -2098,7 +1907,7 @@ public final class SWF {
|
||||
if (f.functionName.isEmpty()) { //anonymous function, leave as is
|
||||
continue;
|
||||
}
|
||||
String changed = deobfuscateName(f.functionName, false, "function", renameType, selected);
|
||||
String changed = deobfuscation.deobfuscateName(f.functionName, false, "function", deobfuscated, renameType, selected);
|
||||
if (changed != null) {
|
||||
f.replacedFunctionName = changed;
|
||||
ret++;
|
||||
@@ -2109,7 +1918,7 @@ public final class SWF {
|
||||
if (f.functionName.isEmpty()) { //anonymous function, leave as is
|
||||
continue;
|
||||
}
|
||||
String changed = deobfuscateName(f.functionName, false, "function", renameType, selected);
|
||||
String changed = deobfuscation.deobfuscateName(f.functionName, false, "function", deobfuscated, renameType, selected);
|
||||
if (changed != null) {
|
||||
f.replacedFunctionName = changed;
|
||||
ret++;
|
||||
@@ -2132,7 +1941,7 @@ public final class SWF {
|
||||
for (MyEntry<DirectValueActionItem, ConstantPool> it : allVariableNames) {
|
||||
vc++;
|
||||
String name = it.key.toStringNoH(it.value);
|
||||
String changed = deobfuscateName(name, false, usageTypes.get(it.key), renameType, selected);
|
||||
String changed = deobfuscation.deobfuscateName(name, false, usageTypes.get(it.key), deobfuscated, renameType, selected);
|
||||
if (changed != null) {
|
||||
boolean addNew = false;
|
||||
String h = System.identityHashCode(it.key) + "_" + name;
|
||||
@@ -2169,7 +1978,7 @@ public final class SWF {
|
||||
actionsMap.put(src, Action.removeNops(0, actionsMap.get(src), version, 0, ""/*FIXME path*/));
|
||||
src.setActions(actionsMap.get(src), version);
|
||||
}
|
||||
deobfuscateInstanceNames(renameType, tags, selected);
|
||||
deobfuscation.deobfuscateInstanceNames(deobfuscated, renameType, tags, selected);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -604,7 +604,7 @@ public class SWFInputStream extends InputStream {
|
||||
* @return List of tags
|
||||
* @throws IOException
|
||||
*/
|
||||
public List<Tag> readTagList(SWF swf, int level, boolean parallel) throws IOException {
|
||||
public List<Tag> readTagList(SWF swf, int level, boolean parallel) throws IOException, InterruptedException {
|
||||
return readTagList(swf, level, parallel, false);
|
||||
}
|
||||
|
||||
@@ -619,7 +619,7 @@ public class SWFInputStream extends InputStream {
|
||||
* @return List of tags
|
||||
* @throws IOException
|
||||
*/
|
||||
public List<Tag> readTagList(SWF swf, int level, boolean parallel, boolean skipUnusualTags) throws IOException {
|
||||
public List<Tag> readTagList(SWF swf, int level, boolean parallel, boolean skipUnusualTags) throws IOException, InterruptedException {
|
||||
return readTagList(swf, level, parallel, skipUnusualTags, true);
|
||||
}
|
||||
|
||||
@@ -635,11 +635,11 @@ public class SWFInputStream extends InputStream {
|
||||
* @return List of tags
|
||||
* @throws IOException
|
||||
*/
|
||||
public List<Tag> readTagList(SWF swf, int level, boolean parallel, boolean skipUnusualTags, boolean parseTags) throws IOException {
|
||||
public List<Tag> readTagList(SWF swf, int level, boolean parallel, boolean skipUnusualTags, boolean parseTags) throws IOException, InterruptedException {
|
||||
ExecutorService executor = null;
|
||||
List<Future<Tag>> futureResults = new ArrayList<>();
|
||||
if (parallel) {
|
||||
executor = Executors.newFixedThreadPool(20);
|
||||
executor = Executors.newFixedThreadPool(Configuration.parallelThreadCount.get());
|
||||
futureResults = new ArrayList<>();
|
||||
}
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
@@ -723,7 +723,9 @@ public class SWFInputStream extends InputStream {
|
||||
for (Future<Tag> future : futureResults) {
|
||||
try {
|
||||
tags.add(future.get());
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
} catch (InterruptedException ex) {
|
||||
future.cancel(true);
|
||||
} catch (ExecutionException e) {
|
||||
Logger.getLogger(SWFInputStream.class.getName()).log(Level.SEVERE, "Error during tag reading", e);
|
||||
}
|
||||
}
|
||||
@@ -733,7 +735,7 @@ public class SWFInputStream extends InputStream {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public static Tag resolveTag(SWF swf, Tag tag, int version, int level, boolean parallel, boolean skipUnusualTags) {
|
||||
public static Tag resolveTag(SWF swf, Tag tag, int version, int level, boolean parallel, boolean skipUnusualTags) throws InterruptedException {
|
||||
Tag ret;
|
||||
|
||||
byte[] data = tag.getData(version);
|
||||
@@ -1023,7 +1025,7 @@ public class SWFInputStream extends InputStream {
|
||||
* @return Tag or null when End tag
|
||||
* @throws IOException
|
||||
*/
|
||||
public Tag readTag(SWF swf, int level, long pos, boolean resolve, boolean parallel, boolean skipUnusualTags) throws IOException {
|
||||
public Tag readTag(SWF swf, int level, long pos, boolean resolve, boolean parallel, boolean skipUnusualTags) throws IOException, InterruptedException {
|
||||
int tagIDTagLength = readUI16();
|
||||
int tagID = (tagIDTagLength) >> 6;
|
||||
if (tagID == 0) {
|
||||
|
||||
@@ -50,6 +50,7 @@ import com.jpexs.decompiler.flash.tags.base.ContainerItem;
|
||||
import com.jpexs.decompiler.flash.tags.base.Exportable;
|
||||
import com.jpexs.decompiler.graph.ExportMode;
|
||||
import com.jpexs.decompiler.graph.TranslateException;
|
||||
import com.jpexs.helpers.CancellableWorker;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -260,7 +261,7 @@ public class TagNode {
|
||||
|
||||
public static List<File> exportNodeAS(final List<Tag> allTags, final AbortRetryIgnoreHandler handler, final List<TagNode> nodeList, final String outdir, final ExportMode exportMode, final EventListener ev) throws IOException {
|
||||
try {
|
||||
List<File> result = Helper.timedCall(new Callable<List<File>>() {
|
||||
List<File> result = CancellableWorker.call(new Callable<List<File>>() {
|
||||
|
||||
@Override
|
||||
public List<File> call() throws Exception {
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.abc;
|
||||
|
||||
import com.jpexs.decompiler.flash.EventListener;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.AVM2Deobfuscation;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.ConstantPool;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.UnknownInstructionCode;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
|
||||
@@ -43,13 +44,10 @@ import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ABC {
|
||||
|
||||
@@ -67,6 +65,7 @@ public class ABC {
|
||||
public static final int MINORwithDECIMAL = 17;
|
||||
protected HashSet<EventListener> listeners = new HashSet<>();
|
||||
private static final Logger logger = Logger.getLogger(ABC.class.getName());
|
||||
private AVM2Deobfuscation deobfuscation;
|
||||
|
||||
public int addMethodBody(MethodBody body) {
|
||||
bodies = Arrays.copyOf(bodies, bodies.length + 1);
|
||||
@@ -111,7 +110,7 @@ public class ABC {
|
||||
return rem;
|
||||
}
|
||||
|
||||
public int removeDeadCode() {
|
||||
public int removeDeadCode() throws InterruptedException {
|
||||
int rem = 0;
|
||||
for (MethodBody body : bodies) {
|
||||
rem += body.removeDeadCode(constants, null/*FIXME*/, method_info[body.method_info]);
|
||||
@@ -119,7 +118,7 @@ public class ABC {
|
||||
return rem;
|
||||
}
|
||||
|
||||
public void restoreControlFlow() {
|
||||
public void restoreControlFlow() throws InterruptedException {
|
||||
for (MethodBody body : bodies) {
|
||||
body.restoreControlFlow(constants, null/*FIXME*/, method_info[body.method_info]);
|
||||
}
|
||||
@@ -238,14 +237,14 @@ public class ABC {
|
||||
for (int i = 0; i < instance_info.length; i++) {
|
||||
informListeners("deobfuscate", "class " + i + "/" + instance_info.length);
|
||||
if (instance_info[i].name_index != 0) {
|
||||
constants.getMultiname(instance_info[i].name_index).name_index = deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(instance_info[i].name_index).name_index, true, renameType);
|
||||
constants.getMultiname(instance_info[i].name_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(instance_info[i].name_index).name_index, true, renameType);
|
||||
if (constants.getMultiname(instance_info[i].name_index).namespace_index != 0) {
|
||||
constants.getNamespace(constants.getMultiname(instance_info[i].name_index).namespace_index).name_index =
|
||||
deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(constants.getMultiname(instance_info[i].name_index).namespace_index).name_index, renameType);
|
||||
deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(constants.getMultiname(instance_info[i].name_index).namespace_index).name_index, renameType);
|
||||
}
|
||||
}
|
||||
if (instance_info[i].super_index != 0) {
|
||||
constants.getMultiname(instance_info[i].super_index).name_index = deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(instance_info[i].super_index).name_index, true, renameType);
|
||||
constants.getMultiname(instance_info[i].super_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(instance_info[i].super_index).name_index, true, renameType);
|
||||
}
|
||||
}
|
||||
if (classesOnly) {
|
||||
@@ -253,14 +252,14 @@ public class ABC {
|
||||
}
|
||||
for (int i = 1; i < constants.getMultinameCount(); i++) {
|
||||
informListeners("deobfuscate", "name " + i + "/" + constants.getMultinameCount());
|
||||
constants.getMultiname(i).name_index = deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(i).name_index, false, renameType);
|
||||
constants.getMultiname(i).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(i).name_index, false, renameType);
|
||||
}
|
||||
for (int i = 1; i < constants.getNamespaceCount(); i++) {
|
||||
informListeners("deobfuscate", "namespace " + i + "/" + constants.getNamespaceCount());
|
||||
if (constants.getNamespace(i).kind != Namespace.KIND_PACKAGE) { //only packages
|
||||
continue;
|
||||
}
|
||||
constants.getNamespace(i).name_index = deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(i).name_index, renameType);
|
||||
constants.getNamespace(i).name_index = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(i).name_index, renameType);
|
||||
}
|
||||
|
||||
//process reflection using getDefinitionByName too
|
||||
@@ -283,11 +282,11 @@ public class ABC {
|
||||
}
|
||||
if (!pkg.isEmpty()) {
|
||||
int pkgStrIndex = constants.getStringId(pkg, true);
|
||||
pkgStrIndex = deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, pkgStrIndex, renameType);
|
||||
pkgStrIndex = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, pkgStrIndex, renameType);
|
||||
pkg = constants.getString(pkgStrIndex);
|
||||
}
|
||||
int nameStrIndex = constants.getStringId(name, true);
|
||||
nameStrIndex = deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, nameStrIndex, true, renameType);
|
||||
nameStrIndex = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, nameStrIndex, true, renameType);
|
||||
name = constants.getString(nameStrIndex);
|
||||
String fullChanged = "";
|
||||
if (!pkg.isEmpty()) {
|
||||
@@ -311,6 +310,7 @@ public class ABC {
|
||||
major_version = ais.readU16();
|
||||
logger.log(Level.FINE, "ABC minor_version: {0}, major_version: {1}", new Object[]{minor_version, major_version});
|
||||
constants = new ConstantPool();
|
||||
deobfuscation = new AVM2Deobfuscation(constants);
|
||||
//constant integers
|
||||
int constant_int_pool_count = ais.readU30();
|
||||
constants.constant_int = new ArrayList<>(constant_int_pool_count);
|
||||
@@ -738,21 +738,11 @@ public class ABC {
|
||||
}
|
||||
}
|
||||
|
||||
public String builtInNs(String ns) {
|
||||
if (ns.equals("http://www.adobe.com/2006/actionscript/flash/proxy")) {
|
||||
return "flash.utils.flash_proxy";
|
||||
}
|
||||
if (ns.equals("http://adobe.com/AS3/2006/builtin")) {
|
||||
return "-";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String nsValueToName(String value) {
|
||||
if (namespaceMap.containsKey(value)) {
|
||||
return namespaceMap.get(value);
|
||||
} else {
|
||||
String ns = builtInNs(value);
|
||||
String ns = deobfuscation.builtInNs(value);
|
||||
if (ns == null) {
|
||||
return "";
|
||||
} else {
|
||||
@@ -792,198 +782,6 @@ public class ABC {
|
||||
output.println("MethodBody[" + i + "]:"); //+ bodies[i].toString(this, constants, method_info));
|
||||
}
|
||||
}
|
||||
public static final String[] reservedWords = {
|
||||
"as", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "each", "else",
|
||||
"extends", "false", "finally", "for", "function", "get", "if", "implements", "import", "in", "instanceof",
|
||||
"interface", "internal", "is", "native", "new", "null", "override", "package", "private", "protected", "public",
|
||||
"return", "set", "super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while",
|
||||
"with", "dynamic", "default", "final", "in", "static"};
|
||||
public static final String validFirstCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
|
||||
public static final String validNextCharacters = validFirstCharacters + "0123456789";
|
||||
public static final String validNsCharacters = ".:$";
|
||||
public static final String fooCharacters = "bcdfghjklmnpqrstvwz";
|
||||
public static final String fooJoinCharacters = "aeiouy";
|
||||
private Random rnd = new Random();
|
||||
private final int DEFAULT_FOO_SIZE = 10;
|
||||
private Map<String, Integer> usageTypesCount = new HashMap<>();
|
||||
|
||||
private String fooString(HashMap<String, String> deobfuscated, String orig, boolean firstUppercase, int rndSize, String usageType, RenameType renameType) {
|
||||
boolean exists;
|
||||
String ret;
|
||||
int pos = 0;
|
||||
|
||||
if (usageType == null) {
|
||||
usageType = "name";
|
||||
}
|
||||
if (usageTypesCount.containsKey(usageType)) {
|
||||
pos = usageTypesCount.get(usageType);
|
||||
}
|
||||
|
||||
loopfoo:
|
||||
do {
|
||||
exists = false;
|
||||
ret = "";
|
||||
if (renameType == RenameType.TYPENUMBER) {
|
||||
pos++;
|
||||
ret = usageType + "_" + pos;
|
||||
} else if (renameType == RenameType.RANDOMWORD) {
|
||||
int len = 3 + rnd.nextInt(rndSize - 3);
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
String c = "";
|
||||
if ((i % 2) == 0) {
|
||||
c = "" + fooCharacters.charAt(rnd.nextInt(fooCharacters.length()));
|
||||
} else {
|
||||
c = "" + fooJoinCharacters.charAt(rnd.nextInt(fooJoinCharacters.length()));
|
||||
}
|
||||
if (i == 0 && firstUppercase) {
|
||||
c = c.toUpperCase(Locale.ENGLISH);
|
||||
}
|
||||
ret += c;
|
||||
}
|
||||
}
|
||||
for (int i = 1; i < constants.getStringCount(); i++) {
|
||||
if (constants.getString(i).equals(ret)) {
|
||||
exists = true;
|
||||
rndSize += 1;
|
||||
continue loopfoo;
|
||||
}
|
||||
}
|
||||
if (isReserved(ret)) {
|
||||
exists = true;
|
||||
rndSize += 1;
|
||||
continue;
|
||||
}
|
||||
if (deobfuscated.containsValue(ret)) {
|
||||
exists = true;
|
||||
rndSize += 1;
|
||||
continue;
|
||||
}
|
||||
} while (exists);
|
||||
usageTypesCount.put(usageType, pos);
|
||||
deobfuscated.put(orig, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private boolean isReserved(String s) {
|
||||
for (String rw : reservedWords) {
|
||||
if (rw.equals(s.trim())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isValidNSPart(String s) {
|
||||
boolean isValid = true;
|
||||
if (isReserved(s)) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (s.charAt(i) > 127) {
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isValid) {
|
||||
Pattern pat = Pattern.compile("^([" + Pattern.quote(validFirstCharacters) + "]" + "[" + Pattern.quote(validFirstCharacters + validNextCharacters + validNsCharacters) + "]*)*$");
|
||||
if (!pat.matcher(s).matches()) {
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public int deobfuscatePackageName(Map<Integer, String> stringUsageTypes, Set<Integer> stringUsages, HashMap<String, String> namesMap, int strIndex, RenameType renameType) {
|
||||
if (strIndex <= 0) {
|
||||
return strIndex;
|
||||
}
|
||||
String s = constants.getString(strIndex);
|
||||
if (builtInNs(s) != null) {
|
||||
return strIndex;
|
||||
}
|
||||
boolean isValid = isValidNSPart(s);
|
||||
if (!isValid) {
|
||||
String newName;
|
||||
if (namesMap.containsKey(s)) {
|
||||
newName = constants.setString(strIndex, namesMap.get(s));
|
||||
} else {
|
||||
String[] parts = null;
|
||||
if (s.contains(".")) {
|
||||
parts = s.split("\\.");
|
||||
} else {
|
||||
parts = new String[]{s};
|
||||
}
|
||||
String ret = "";
|
||||
for (int p = 0; p < parts.length; p++) {
|
||||
if (p > 0) {
|
||||
ret += ".";
|
||||
}
|
||||
if (!isValidNSPart(parts[p])) {
|
||||
ret += fooString(namesMap, parts[p], false, DEFAULT_FOO_SIZE, "package", renameType);
|
||||
} else {
|
||||
ret += parts[p];
|
||||
}
|
||||
}
|
||||
newName = ret;
|
||||
namesMap.put(s, newName);
|
||||
}
|
||||
if (stringUsages.contains(strIndex)) {
|
||||
strIndex = constants.addString(newName);
|
||||
} else {
|
||||
constants.setString(strIndex, newName);
|
||||
}
|
||||
|
||||
}
|
||||
return strIndex;
|
||||
}
|
||||
|
||||
public int deobfuscateName(Map<Integer, String> stringUsageTypes, Set<Integer> stringUsages, Set<Integer> namespaceUsages, HashMap<String, String> namesMap, int strIndex, boolean firstUppercase, RenameType renameType) {
|
||||
if (strIndex <= 0) {
|
||||
return strIndex;
|
||||
}
|
||||
String s = constants.getString(strIndex);
|
||||
boolean isValid = true;
|
||||
if (isReserved(s)) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (s.charAt(i) > 127) {
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
Pattern pat = Pattern.compile("^[" + Pattern.quote(validFirstCharacters) + "]" + "[" + Pattern.quote(validFirstCharacters + validNextCharacters) + "]*$");
|
||||
if (!pat.matcher(s).matches()) {
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
String newname;
|
||||
if (namesMap.containsKey(s)) {
|
||||
newname = namesMap.get(s);
|
||||
} else {
|
||||
newname = fooString(namesMap, constants.getString(strIndex), firstUppercase, DEFAULT_FOO_SIZE, stringUsageTypes.get(strIndex), renameType);
|
||||
}
|
||||
if (stringUsages.contains(strIndex) || namespaceUsages.contains(strIndex)) { //this name is already referenced as String
|
||||
strIndex = constants.addString(s); //add new index
|
||||
}
|
||||
constants.setString(strIndex, newname);
|
||||
if (!namesMap.containsKey(s)) {
|
||||
namesMap.put(s, constants.getString(strIndex));
|
||||
}
|
||||
}
|
||||
return strIndex;
|
||||
}
|
||||
|
||||
private void checkMultinameUsedInMethod(int multinameIndex, int methodInfo, List<MultinameUsage> ret, int classIndex, int traitIndex, boolean isStatic, boolean isInitializer, Traits traits, int parentTraitIndex) {
|
||||
for (int p = 0; p < method_info[methodInfo].param_types.length; p++) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
|
||||
import com.jpexs.decompiler.flash.helpers.NulWriter;
|
||||
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
|
||||
import com.jpexs.decompiler.graph.ExportMode;
|
||||
import com.jpexs.helpers.CancellableWorker;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -143,7 +144,7 @@ public class ScriptPack {
|
||||
public void toSource(GraphTextWriter writer, final List<ABCContainerTag> abcList, final Trait[] traits, final ExportMode exportMode, final boolean parallel) throws InterruptedException {
|
||||
writer.suspendMeasure();
|
||||
try {
|
||||
Helper.timedCall(new Callable<Void>() {
|
||||
CancellableWorker.call(new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
convert(new NulWriter(), abcList, traits, exportMode, parallel);
|
||||
@@ -159,7 +160,7 @@ public class ScriptPack {
|
||||
writer.appendNoHilight(" */").newLine();
|
||||
writer.appendNoHilight("throw new IllegalOperationError(\"Not decompiled due to timeout\");").newLine();
|
||||
return;
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
} catch (ExecutionException ex) {
|
||||
writer.continueMeasure();
|
||||
Logger.getLogger(MethodBody.class.getName()).log(Level.SEVERE, "Decompilation error", ex);
|
||||
writer.appendNoHilight("/*").newLine();
|
||||
|
||||
@@ -1796,7 +1796,10 @@ public class AVM2Code implements Serializable {
|
||||
return stats;
|
||||
}
|
||||
|
||||
private void visitCode(int ip, int lastIp, HashMap<Integer, List<Integer>> refs) {
|
||||
private void visitCode(int ip, int lastIp, HashMap<Integer, List<Integer>> refs) throws InterruptedException {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
while (ip < code.size()) {
|
||||
if (!refs.containsKey(ip)) {
|
||||
refs.put(ip, new ArrayList<Integer>());
|
||||
@@ -1844,7 +1847,7 @@ public class AVM2Code implements Serializable {
|
||||
};
|
||||
}
|
||||
|
||||
public HashMap<Integer, List<Integer>> visitCode(MethodBody body) {
|
||||
public HashMap<Integer, List<Integer>> visitCode(MethodBody body) throws InterruptedException {
|
||||
HashMap<Integer, List<Integer>> refs = new HashMap<>();
|
||||
for (int i = 0; i < code.size(); i++) {
|
||||
refs.put(i, new ArrayList<Integer>());
|
||||
@@ -2078,7 +2081,7 @@ public class AVM2Code implements Serializable {
|
||||
|
||||
}
|
||||
|
||||
private void restoreControlFlowPass(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body, boolean secondpass) {
|
||||
private void restoreControlFlowPass(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body, boolean secondpass) throws InterruptedException {
|
||||
try {
|
||||
HashMap<Integer, List<Integer>> refs;
|
||||
int[] visited2 = new int[code.size()];
|
||||
@@ -2134,7 +2137,7 @@ public class AVM2Code implements Serializable {
|
||||
removeDeadCode(constants, trait, info, body);
|
||||
}
|
||||
|
||||
public void restoreControlFlow(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) {
|
||||
public void restoreControlFlow(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException {
|
||||
restoreControlFlowPass(constants, trait, info, body, false);
|
||||
//restoreControlFlowPass(constants, body, true);
|
||||
}
|
||||
@@ -2146,7 +2149,7 @@ public class AVM2Code implements Serializable {
|
||||
}
|
||||
}
|
||||
}*/
|
||||
public void removeIgnored(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) {
|
||||
public void removeIgnored(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException {
|
||||
try {
|
||||
List<Integer> outputMap = new ArrayList<>();
|
||||
HilightedTextWriter writer = new HilightedTextWriter(false);
|
||||
@@ -2170,7 +2173,7 @@ public class AVM2Code implements Serializable {
|
||||
invalidateCache();
|
||||
}
|
||||
|
||||
public int removeDeadCode(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) {
|
||||
public int removeDeadCode(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException {
|
||||
HashMap<Integer, List<Integer>> refs = visitCode(body);
|
||||
|
||||
int cnt = 0;
|
||||
@@ -2377,7 +2380,7 @@ public class AVM2Code implements Serializable {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static int removeTraps(HashMap<Integer, List<Integer>> refs, boolean secondPass, boolean indeterminate, List<Object> localData, Stack<GraphTargetItem> stack, List<GraphTargetItem> output, AVM2GraphSource code, int ip, HashMap<Integer, Integer> visited, HashMap<Integer, HashMap<Integer, GraphTargetItem>> visitedStates, HashMap<AVM2Instruction, Decision> decisions, String path, int recursionLevel) throws InterruptedException {
|
||||
if (Thread.interrupted()) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
if (recursionLevel > code.size() + 1) {
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2013 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* 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.abc.avm2;
|
||||
|
||||
import com.jpexs.decompiler.flash.abc.RenameType;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class AVM2Deobfuscation {
|
||||
|
||||
private static Random rnd = new Random();
|
||||
private static final int DEFAULT_FOO_SIZE = 10;
|
||||
|
||||
public static final String[] reservedWords = {
|
||||
"as", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "each", "else",
|
||||
"extends", "false", "finally", "for", "function", "get", "if", "implements", "import", "in", "instanceof",
|
||||
"interface", "internal", "is", "native", "new", "null", "override", "package", "private", "protected", "public",
|
||||
"return", "set", "super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while",
|
||||
"with", "dynamic", "default", "final", "in", "static"};
|
||||
public static final String validFirstCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
|
||||
public static final String validNextCharacters = validFirstCharacters + "0123456789";
|
||||
public static final String validNsCharacters = ".:$";
|
||||
public static final String fooCharacters = "bcdfghjklmnpqrstvwz";
|
||||
public static final String fooJoinCharacters = "aeiouy";
|
||||
|
||||
private ConstantPool constants;
|
||||
private Map<String, Integer> usageTypesCount = new HashMap<>();
|
||||
|
||||
public AVM2Deobfuscation(ConstantPool constants) {
|
||||
this.constants = constants;
|
||||
}
|
||||
|
||||
private static boolean isReserved(String s) {
|
||||
for (String rw : reservedWords) {
|
||||
if (rw.equals(s.trim())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isValidNSPart(String s) {
|
||||
boolean isValid = true;
|
||||
if (isReserved(s)) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (s.charAt(i) > 127) {
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isValid) {
|
||||
Pattern pat = Pattern.compile("^([" + Pattern.quote(validFirstCharacters) + "]" + "[" + Pattern.quote(validFirstCharacters + validNextCharacters + validNsCharacters) + "]*)*$");
|
||||
if (!pat.matcher(s).matches()) {
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public String builtInNs(String ns) {
|
||||
if (ns.equals("http://www.adobe.com/2006/actionscript/flash/proxy")) {
|
||||
return "flash.utils.flash_proxy";
|
||||
}
|
||||
if (ns.equals("http://adobe.com/AS3/2006/builtin")) {
|
||||
return "-";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String fooString(HashMap<String, String> deobfuscated, String orig, boolean firstUppercase, int rndSize, String usageType, RenameType renameType) {
|
||||
boolean exists;
|
||||
String ret;
|
||||
int pos = 0;
|
||||
|
||||
if (usageType == null) {
|
||||
usageType = "name";
|
||||
}
|
||||
if (usageTypesCount.containsKey(usageType)) {
|
||||
pos = usageTypesCount.get(usageType);
|
||||
}
|
||||
|
||||
loopfoo:
|
||||
do {
|
||||
exists = false;
|
||||
ret = "";
|
||||
if (renameType == RenameType.TYPENUMBER) {
|
||||
pos++;
|
||||
ret = usageType + "_" + pos;
|
||||
} else if (renameType == RenameType.RANDOMWORD) {
|
||||
int len = 3 + rnd.nextInt(rndSize - 3);
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
String c = "";
|
||||
if ((i % 2) == 0) {
|
||||
c = "" + fooCharacters.charAt(rnd.nextInt(fooCharacters.length()));
|
||||
} else {
|
||||
c = "" + fooJoinCharacters.charAt(rnd.nextInt(fooJoinCharacters.length()));
|
||||
}
|
||||
if (i == 0 && firstUppercase) {
|
||||
c = c.toUpperCase(Locale.ENGLISH);
|
||||
}
|
||||
ret += c;
|
||||
}
|
||||
}
|
||||
for (int i = 1; i < constants.getStringCount(); i++) {
|
||||
if (constants.getString(i).equals(ret)) {
|
||||
exists = true;
|
||||
rndSize += 1;
|
||||
continue loopfoo;
|
||||
}
|
||||
}
|
||||
if (isReserved(ret)) {
|
||||
exists = true;
|
||||
rndSize += 1;
|
||||
continue;
|
||||
}
|
||||
if (deobfuscated.containsValue(ret)) {
|
||||
exists = true;
|
||||
rndSize += 1;
|
||||
continue;
|
||||
}
|
||||
} while (exists);
|
||||
usageTypesCount.put(usageType, pos);
|
||||
deobfuscated.put(orig, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int deobfuscatePackageName(Map<Integer, String> stringUsageTypes, Set<Integer> stringUsages, HashMap<String, String> namesMap, int strIndex, RenameType renameType) {
|
||||
if (strIndex <= 0) {
|
||||
return strIndex;
|
||||
}
|
||||
String s = constants.getString(strIndex);
|
||||
if (builtInNs(s) != null) {
|
||||
return strIndex;
|
||||
}
|
||||
boolean isValid = isValidNSPart(s);
|
||||
if (!isValid) {
|
||||
String newName;
|
||||
if (namesMap.containsKey(s)) {
|
||||
newName = constants.setString(strIndex, namesMap.get(s));
|
||||
} else {
|
||||
String[] parts = null;
|
||||
if (s.contains(".")) {
|
||||
parts = s.split("\\.");
|
||||
} else {
|
||||
parts = new String[]{s};
|
||||
}
|
||||
String ret = "";
|
||||
for (int p = 0; p < parts.length; p++) {
|
||||
if (p > 0) {
|
||||
ret += ".";
|
||||
}
|
||||
if (!isValidNSPart(parts[p])) {
|
||||
ret += fooString(namesMap, parts[p], false, DEFAULT_FOO_SIZE, "package", renameType);
|
||||
} else {
|
||||
ret += parts[p];
|
||||
}
|
||||
}
|
||||
newName = ret;
|
||||
namesMap.put(s, newName);
|
||||
}
|
||||
if (stringUsages.contains(strIndex)) {
|
||||
strIndex = constants.addString(newName);
|
||||
} else {
|
||||
constants.setString(strIndex, newName);
|
||||
}
|
||||
|
||||
}
|
||||
return strIndex;
|
||||
}
|
||||
|
||||
public int deobfuscateName(Map<Integer, String> stringUsageTypes, Set<Integer> stringUsages, Set<Integer> namespaceUsages, HashMap<String, String> namesMap, int strIndex, boolean firstUppercase, RenameType renameType) {
|
||||
if (strIndex <= 0) {
|
||||
return strIndex;
|
||||
}
|
||||
String s = constants.getString(strIndex);
|
||||
boolean isValid = true;
|
||||
if (isReserved(s)) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (s.charAt(i) > 127) {
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
Pattern pat = Pattern.compile("^[" + Pattern.quote(validFirstCharacters) + "]" + "[" + Pattern.quote(validFirstCharacters + validNextCharacters) + "]*$");
|
||||
if (!pat.matcher(s).matches()) {
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
String newname;
|
||||
if (namesMap.containsKey(s)) {
|
||||
newname = namesMap.get(s);
|
||||
} else {
|
||||
newname = fooString(namesMap, constants.getString(strIndex), firstUppercase, DEFAULT_FOO_SIZE, stringUsageTypes.get(strIndex), renameType);
|
||||
}
|
||||
if (stringUsages.contains(strIndex) || namespaceUsages.contains(strIndex)) { //this name is already referenced as String
|
||||
strIndex = constants.addString(s); //add new index
|
||||
}
|
||||
constants.setString(strIndex, newname);
|
||||
if (!namesMap.containsKey(s)) {
|
||||
namesMap.put(s, constants.getString(strIndex));
|
||||
}
|
||||
}
|
||||
return strIndex;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
|
||||
import com.jpexs.decompiler.flash.abc.types.MethodBody;
|
||||
import com.jpexs.decompiler.graph.GraphPart;
|
||||
import com.jpexs.decompiler.graph.GraphSource;
|
||||
import com.jpexs.decompiler.graph.GraphSourceItem;
|
||||
import com.jpexs.decompiler.graph.GraphTargetItem;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
@@ -73,7 +73,7 @@ public class ASM3Parser {
|
||||
}
|
||||
}
|
||||
|
||||
public static AVM2Code parse(Reader reader, ConstantPool constants, Trait trait, MethodBody body, MethodInfo info) throws IOException, ParseException {
|
||||
public static AVM2Code parse(Reader reader, ConstantPool constants, Trait trait, MethodBody body, MethodInfo info) throws IOException, ParseException, InterruptedException {
|
||||
return parse(reader, constants, trait, null, body, info);
|
||||
}
|
||||
|
||||
@@ -473,7 +473,7 @@ public class ASM3Parser {
|
||||
return new ValueKind(value_index, value_kind);
|
||||
}
|
||||
|
||||
public static AVM2Code parse(Reader reader, ConstantPool constants, Trait trait, MissingSymbolHandler missingHandler, MethodBody body, MethodInfo info) throws IOException, ParseException {
|
||||
public static AVM2Code parse(Reader reader, ConstantPool constants, Trait trait, MissingSymbolHandler missingHandler, MethodBody body, MethodInfo info) throws IOException, ParseException, InterruptedException {
|
||||
AVM2Code code = new AVM2Code();
|
||||
|
||||
List<OffsetItem> offsetItems = new ArrayList<>();
|
||||
@@ -909,6 +909,9 @@ public class ASM3Parser {
|
||||
|
||||
for (OffsetItem oi : offsetItems) {
|
||||
for (LabelItem li : labelItems) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
if (oi.label.equals(li.label)) {
|
||||
AVM2Instruction ins = code.code.get((int) oi.insPosition);
|
||||
int relOffset;
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.jpexs.decompiler.graph.ExportMode;
|
||||
import com.jpexs.decompiler.graph.Graph;
|
||||
import com.jpexs.decompiler.graph.GraphTargetItem;
|
||||
import com.jpexs.decompiler.graph.model.LocalData;
|
||||
import com.jpexs.helpers.CancellableWorker;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
@@ -76,11 +77,11 @@ public class MethodBody implements Cloneable, Serializable {
|
||||
return s;
|
||||
}
|
||||
|
||||
public int removeDeadCode(ConstantPool constants, Trait trait, MethodInfo info) {
|
||||
public int removeDeadCode(ConstantPool constants, Trait trait, MethodInfo info) throws InterruptedException {
|
||||
return code.removeDeadCode(constants, trait, info, this);
|
||||
}
|
||||
|
||||
public void restoreControlFlow(ConstantPool constants, Trait trait, MethodInfo info) {
|
||||
public void restoreControlFlow(ConstantPool constants, Trait trait, MethodInfo info) throws InterruptedException {
|
||||
code.restoreControlFlow(constants, trait, info, this);
|
||||
}
|
||||
|
||||
@@ -139,7 +140,7 @@ public class MethodBody implements Cloneable, Serializable {
|
||||
}
|
||||
};
|
||||
if (firstLevel) {
|
||||
Helper.timedCall(callable, timeout, TimeUnit.SECONDS);
|
||||
CancellableWorker.call(callable, timeout, TimeUnit.SECONDS);
|
||||
} else {
|
||||
callable.call();
|
||||
}
|
||||
|
||||
@@ -437,12 +437,12 @@ public class TraitClass extends Trait implements TraitWithSlot {
|
||||
int bodyIndex = abc.findBodyIndex(abc.class_info[class_info].cinit_index);
|
||||
if (bodyIndex != -1) {
|
||||
if (!classInitializerIsEmpty) {
|
||||
writer.newLine();
|
||||
writer.startTrait(abc.class_info[class_info].static_traits.traits.length + abc.instance_info[class_info].instance_traits.traits.length + 1);
|
||||
writer.appendNoHilight("{").newLine();
|
||||
abc.bodies[bodyIndex].toString(path +/*packageName +*/ "/" + abc.instance_info[class_info].getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".staticinitializer", exportMode, true, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack<GraphTargetItem>(), true, writer, fullyQualifiedNames, abc.class_info[class_info].static_traits);
|
||||
writer.appendNoHilight("}").newLine();
|
||||
writer.endTrait();
|
||||
writer.newLine();
|
||||
}
|
||||
} else {
|
||||
//"/*classInitializer*/";
|
||||
@@ -465,6 +465,7 @@ public class TraitClass extends Trait implements TraitWithSlot {
|
||||
}
|
||||
}
|
||||
|
||||
writer.newLine();
|
||||
writer.startTrait(abc.class_info[class_info].static_traits.traits.length + abc.instance_info[class_info].instance_traits.traits.length);
|
||||
writer.appendNoHilight(modifier);
|
||||
writer.appendNoHilight("function ");
|
||||
@@ -482,7 +483,6 @@ public class TraitClass extends Trait implements TraitWithSlot {
|
||||
}
|
||||
writer.appendNoHilight("}").newLine();
|
||||
writer.endTrait();
|
||||
writer.newLine();
|
||||
}
|
||||
|
||||
//static variables,constants & methods
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.jpexs.decompiler.flash.abc.types.traits;
|
||||
|
||||
import com.jpexs.decompiler.flash.abc.ABC;
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
|
||||
import com.jpexs.decompiler.flash.helpers.NulWriter;
|
||||
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
|
||||
@@ -121,9 +122,7 @@ public class Traits implements Serializable {
|
||||
|
||||
public GraphTextWriter toString(Trait parent, String path, List<ABCContainerTag> abcTags, ABC abc, boolean isStatic, ExportMode exportMode, boolean makePackages, int scriptIndex, int classIndex, GraphTextWriter writer, List<String> fullyQualifiedNames, boolean parallel) throws InterruptedException {
|
||||
for (int t = 0; t < traits.length; t++) {
|
||||
if (t > 0) {
|
||||
writer.newLine();
|
||||
}
|
||||
writer.newLine();
|
||||
Trait trait = traits[t];
|
||||
int h = t;
|
||||
if (classIndex != -1) {
|
||||
@@ -157,7 +156,7 @@ public class Traits implements Serializable {
|
||||
task.call();
|
||||
}
|
||||
} else {
|
||||
ExecutorService executor = Executors.newFixedThreadPool(20);
|
||||
ExecutorService executor = Executors.newFixedThreadPool(Configuration.parallelThreadCount.get());
|
||||
List<Future<Void>> futureResults = null;
|
||||
|
||||
futureResults = new ArrayList<>();
|
||||
@@ -170,7 +169,10 @@ public class Traits implements Serializable {
|
||||
for (int f = 0; f < futureResults.size(); f++) {
|
||||
try {
|
||||
futureResults.get(f).get();
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
} catch (InterruptedException ex) {
|
||||
executor.shutdownNow();
|
||||
throw ex;
|
||||
} catch (ExecutionException ex) {
|
||||
Logger.getLogger(Traits.class.getName()).log(Level.SEVERE, "Error during traits converting", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ import com.jpexs.decompiler.graph.model.IfItem;
|
||||
import com.jpexs.decompiler.graph.model.LocalData;
|
||||
import com.jpexs.decompiler.graph.model.NotItem;
|
||||
import com.jpexs.decompiler.graph.model.ScriptEndItem;
|
||||
import com.jpexs.helpers.CancellableWorker;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -705,7 +706,7 @@ public class Action implements GraphSourceItem {
|
||||
Throwable convertException = null;
|
||||
int timeout = Configuration.decompilationTimeoutSingleMethod.get();
|
||||
try {
|
||||
tree = Helper.timedCall(new Callable<List<GraphTargetItem>>() {
|
||||
tree = CancellableWorker.call(new Callable<List<GraphTargetItem>>() {
|
||||
@Override
|
||||
public List<GraphTargetItem> call() throws Exception {
|
||||
int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC;
|
||||
@@ -714,7 +715,7 @@ public class Action implements GraphSourceItem {
|
||||
return tree;
|
||||
}
|
||||
}, timeout, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException | TimeoutException | ExecutionException | OutOfMemoryError | TranslateException | StackOverflowError ex) {
|
||||
} catch (TimeoutException | ExecutionException | OutOfMemoryError | TranslateException | StackOverflowError ex) {
|
||||
Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error", ex);
|
||||
convertException = ex;
|
||||
if (ex instanceof ExecutionException && ex.getCause() instanceof Exception) {
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2013 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* 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.action;
|
||||
|
||||
import com.jpexs.decompiler.flash.abc.RenameType;
|
||||
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ActionDeobfuscation {
|
||||
|
||||
private Random rnd = new Random();
|
||||
private final int DEFAULT_FOO_SIZE = 10;
|
||||
public HashSet<String> allVariableNamesStr = new HashSet<>();
|
||||
private HashMap<String, Integer> typeCounts = new HashMap<>();
|
||||
|
||||
public static final String[] reservedWords = {
|
||||
"as", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "each", "else",
|
||||
"extends", "false", "finally", "for", "function", "get", "if", "implements", "import", "in", "instanceof",
|
||||
"interface", "internal", "is", "native", "new", "null", "override", "package", "private", "protected", "public",
|
||||
"return", "set", "super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while",
|
||||
"with", "dynamic", "default", "final", "in"};
|
||||
public static final String validFirstCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
|
||||
public static final String validNextCharacters = validFirstCharacters + "0123456789";
|
||||
public static final String fooCharacters = "bcdfghjklmnpqrstvwz";
|
||||
public static final String fooJoinCharacters = "aeiouy";
|
||||
|
||||
private boolean isReserved(String s) {
|
||||
for (String rw : reservedWords) {
|
||||
if (rw.equals(s.trim())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String fooString(HashMap<String, String> deobfuscated, String orig, boolean firstUppercase, int rndSize) {
|
||||
boolean exists;
|
||||
String ret;
|
||||
loopfoo:
|
||||
do {
|
||||
exists = false;
|
||||
int len = 3 + rnd.nextInt(rndSize - 3);
|
||||
ret = "";
|
||||
for (int i = 0; i < len; i++) {
|
||||
String c = "";
|
||||
if ((i % 2) == 0) {
|
||||
c = "" + fooCharacters.charAt(rnd.nextInt(fooCharacters.length()));
|
||||
} else {
|
||||
c = "" + fooJoinCharacters.charAt(rnd.nextInt(fooJoinCharacters.length()));
|
||||
}
|
||||
if (i == 0 && firstUppercase) {
|
||||
c = c.toUpperCase(Locale.ENGLISH);
|
||||
}
|
||||
ret += c;
|
||||
}
|
||||
if (allVariableNamesStr.contains(ret)) {
|
||||
exists = true;
|
||||
rndSize += 1;
|
||||
continue loopfoo;
|
||||
}
|
||||
if (isReserved(ret)) {
|
||||
exists = true;
|
||||
rndSize += 1;
|
||||
continue;
|
||||
}
|
||||
if (deobfuscated.containsValue(ret)) {
|
||||
exists = true;
|
||||
rndSize += 1;
|
||||
continue;
|
||||
}
|
||||
} while (exists);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void deobfuscateInstanceNames(HashMap<String, String> namesMap, RenameType renameType, List<Tag> tags, Map<String, String> selected) {
|
||||
for (Tag t : tags) {
|
||||
if (t instanceof DefineSpriteTag) {
|
||||
deobfuscateInstanceNames(namesMap, renameType, ((DefineSpriteTag) t).subTags, selected);
|
||||
}
|
||||
if (t instanceof PlaceObjectTypeTag) {
|
||||
PlaceObjectTypeTag po = (PlaceObjectTypeTag) t;
|
||||
String name = po.getInstanceName();
|
||||
if (name != null) {
|
||||
String changedName = deobfuscateName(name, false, "instance", namesMap, renameType, selected);
|
||||
if (changedName != null) {
|
||||
po.setInstanceName(changedName);
|
||||
}
|
||||
}
|
||||
String className = po.getClassName();
|
||||
if (className != null) {
|
||||
String changedClassName = deobfuscateNameWithPackage(className, namesMap, renameType, selected);
|
||||
if (changedClassName != null) {
|
||||
po.setClassName(changedClassName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String deobfuscatePackage(String pkg, HashMap<String, String> namesMap, RenameType renameType, Map<String, String> selected) {
|
||||
if (namesMap.containsKey(pkg)) {
|
||||
return namesMap.get(pkg);
|
||||
}
|
||||
String[] parts = null;
|
||||
if (pkg.contains(".")) {
|
||||
parts = pkg.split("\\.");
|
||||
} else {
|
||||
parts = new String[]{pkg};
|
||||
}
|
||||
String ret = "";
|
||||
boolean isChanged = false;
|
||||
for (int p = 0; p < parts.length; p++) {
|
||||
if (p > 0) {
|
||||
ret += ".";
|
||||
}
|
||||
String partChanged = deobfuscateName(parts[p], false, "package", namesMap, renameType, selected);
|
||||
if (partChanged != null) {
|
||||
ret += partChanged;
|
||||
isChanged = true;
|
||||
} else {
|
||||
ret += parts[p];
|
||||
}
|
||||
}
|
||||
if (isChanged) {
|
||||
namesMap.put(pkg, ret);
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String deobfuscateNameWithPackage(String n, HashMap<String, String> namesMap, RenameType renameType, Map<String, String> selected) {
|
||||
String pkg = null;
|
||||
String name = "";
|
||||
if (n.contains(".")) {
|
||||
pkg = n.substring(0, n.lastIndexOf('.'));
|
||||
name = n.substring(n.lastIndexOf('.') + 1);
|
||||
} else {
|
||||
name = n;
|
||||
}
|
||||
boolean changed = false;
|
||||
if ((pkg != null) && (!pkg.isEmpty())) {
|
||||
String changedPkg = deobfuscatePackage(pkg, namesMap, renameType, selected);
|
||||
if (changedPkg != null) {
|
||||
changed = true;
|
||||
pkg = changedPkg;
|
||||
}
|
||||
}
|
||||
String changedName = deobfuscateName(name, true, "class", namesMap, renameType, selected);
|
||||
if (changedName != null) {
|
||||
changed = true;
|
||||
name = changedName;
|
||||
}
|
||||
if (changed) {
|
||||
String newClassName = "";
|
||||
if (pkg == null) {
|
||||
newClassName = name;
|
||||
} else {
|
||||
newClassName = pkg + "." + name;
|
||||
}
|
||||
return newClassName;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String deobfuscateName(String s, boolean firstUppercase, String usageType, HashMap<String, String> namesMap, RenameType renameType, Map<String, String> selected) {
|
||||
boolean isValid = true;
|
||||
if (usageType == null) {
|
||||
usageType = "name";
|
||||
}
|
||||
|
||||
if (selected != null) {
|
||||
if (selected.containsKey(s)) {
|
||||
return selected.get(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (isReserved(s)) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (s.charAt(i) > 127) {
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
Pattern pat = Pattern.compile("^[" + Pattern.quote(validFirstCharacters) + "]" + "[" + Pattern.quote(validFirstCharacters + validNextCharacters) + "]*$");
|
||||
if (!pat.matcher(s).matches()) {
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
if (!isValid) {
|
||||
if (namesMap.containsKey(s)) {
|
||||
return namesMap.get(s);
|
||||
} else {
|
||||
Integer cnt = typeCounts.get(usageType);
|
||||
if (cnt == null) {
|
||||
cnt = 0;
|
||||
}
|
||||
|
||||
String ret = null;
|
||||
if (renameType == RenameType.TYPENUMBER) {
|
||||
|
||||
boolean found;
|
||||
do {
|
||||
found = false;
|
||||
cnt++;
|
||||
ret = usageType + "_" + cnt;
|
||||
found = allVariableNamesStr.contains(ret);
|
||||
} while (found);
|
||||
typeCounts.put(usageType, cnt);
|
||||
} else if (renameType == RenameType.RANDOMWORD) {
|
||||
ret = fooString(namesMap, s, firstUppercase, DEFAULT_FOO_SIZE);
|
||||
}
|
||||
namesMap.put(s, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import com.jpexs.decompiler.graph.GraphTargetItem;
|
||||
import com.jpexs.decompiler.graph.NotCompileTimeItem;
|
||||
import com.jpexs.decompiler.graph.TranslateException;
|
||||
import com.jpexs.decompiler.graph.model.LocalData;
|
||||
import com.jpexs.helpers.CancellableWorker;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import com.jpexs.helpers.MemoryInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -54,6 +55,10 @@ import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Scanner;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -80,7 +85,41 @@ public class ActionListReader {
|
||||
* @return List of actions
|
||||
* @throws IOException
|
||||
*/
|
||||
public static List<Action> readActionList(List<DisassemblyListener> listeners, long containerSWFOffset, MemoryInputStream mis, int version, int ip, int endIp, String path) throws IOException {
|
||||
public static List<Action> readActionListTimeout(final List<DisassemblyListener> listeners, final long containerSWFOffset, final MemoryInputStream mis, final int version, final int ip, final int endIp, final String path) throws IOException, InterruptedException, TimeoutException {
|
||||
try {
|
||||
return CancellableWorker.call(new Callable<List<Action>>() {
|
||||
|
||||
@Override
|
||||
public List<Action> call() throws IOException, InterruptedException {
|
||||
return readActionList(listeners, containerSWFOffset, mis, version, ip, endIp, path);
|
||||
}
|
||||
}, Configuration.decompilationTimeoutSingleMethod.get(), TimeUnit.SECONDS);
|
||||
} catch (ExecutionException ex) {
|
||||
Throwable cause = ex.getCause();
|
||||
if (cause instanceof InterruptedException) {
|
||||
throw (InterruptedException) cause;
|
||||
} else if (cause instanceof InterruptedException) {
|
||||
throw (IOException) cause;
|
||||
} else {
|
||||
Logger.getLogger(ActionListReader.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Reads list of actions from the stream. Reading ends with
|
||||
* ActionEndFlag(=0) or end of the stream.
|
||||
* @param listeners
|
||||
* @param containerSWFOffset
|
||||
* @param mis
|
||||
* @param version
|
||||
* @param ip
|
||||
* @param endIp
|
||||
* @param path
|
||||
* @return List of actions
|
||||
* @throws IOException
|
||||
*/
|
||||
public static List<Action> readActionList(List<DisassemblyListener> listeners, long containerSWFOffset, MemoryInputStream mis, int version, int ip, int endIp, String path) throws IOException, InterruptedException {
|
||||
boolean deobfuscate = Configuration.autoDeobfuscate.get();
|
||||
|
||||
ConstantPool cpool = new ConstantPool();
|
||||
@@ -151,11 +190,7 @@ public class ActionListReader {
|
||||
|
||||
if (deobfuscate) {
|
||||
try {
|
||||
try {
|
||||
actions = deobfuscateActionList(listeners, containerSWFOffset, actions, version, ip, path);
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(ActionListReader.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
actions = deobfuscateActionList(listeners, containerSWFOffset, actions, version, ip, path);
|
||||
updateActionLengths(actions, version);
|
||||
removeZeroJumps(actions, version);
|
||||
} catch (TranslateException ex) {
|
||||
@@ -680,6 +715,10 @@ public class ActionListReader {
|
||||
Scanner sc = new Scanner(System.in);
|
||||
loopip:
|
||||
while (((endip == -1) || (endip > ip)) && (a = actions.get(pos)) != null) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
|
||||
int actionLen = getTotalActionLength(a);
|
||||
pos += actionLen;
|
||||
if (!visited.containsKey(ip)) {
|
||||
|
||||
@@ -53,6 +53,8 @@ public class Configuration {
|
||||
public static final ConfigurationItem<Boolean> decompile = null;
|
||||
@ConfigurationDefaultBoolean(true)
|
||||
public static final ConfigurationItem<Boolean> parallelSpeedUp = null;
|
||||
@ConfigurationDefaultInt(20)
|
||||
public static final ConfigurationItem<Integer> parallelThreadCount = null;
|
||||
@ConfigurationDefaultBoolean(true)
|
||||
public static final ConfigurationItem<Boolean> autoDeobfuscate = null;
|
||||
@ConfigurationDefaultBoolean(true)
|
||||
|
||||
@@ -94,6 +94,10 @@ public class CommandLineArgumentParser {
|
||||
System.out.println(" ...error handling mode. \"abort\" stops the exporting, \"retry\" tries the exporting N times, \"ignore\" ignores the current file");
|
||||
System.out.println(" 11) -timeout N");
|
||||
System.out.println(" ...decompilation timeout for a single method in AS3 or single action in AS1/2 in seconds");
|
||||
System.out.println(" 12) -exportTimeout N");
|
||||
System.out.println(" ...total export timeout in seconds");
|
||||
System.out.println(" 13) -exportFileTimeout N");
|
||||
System.out.println(" ...export timeout for a single AS3 class in seconds");
|
||||
System.out.println();
|
||||
System.out.println("Examples:");
|
||||
System.out.println("java -jar ffdec.jar myfile.swf");
|
||||
@@ -125,6 +129,9 @@ public class CommandLineArgumentParser {
|
||||
OUTER:
|
||||
while (true) {
|
||||
nextParam = args.remove();
|
||||
if (nextParam != null) {
|
||||
nextParam = nextParam.toLowerCase();
|
||||
}
|
||||
switch (nextParam) {
|
||||
case "-config":
|
||||
parseConfig(args);
|
||||
@@ -140,6 +147,12 @@ public class CommandLineArgumentParser {
|
||||
case "-timeout":
|
||||
parseTimeout(args);
|
||||
break;
|
||||
case "-exporttimeout":
|
||||
parseExportTimeout(args);
|
||||
break;
|
||||
case "-exportfiletimeout":
|
||||
parseExportFileTimeout(args);
|
||||
break;
|
||||
case "-affinity":
|
||||
parseAffinity(args);
|
||||
break;
|
||||
@@ -320,6 +333,32 @@ public class CommandLineArgumentParser {
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseExportTimeout(Queue<String> args) {
|
||||
if (args.isEmpty()) {
|
||||
System.err.println("timeout parameter expected");
|
||||
badArguments();
|
||||
}
|
||||
try {
|
||||
int timeout = Integer.parseInt(args.remove());
|
||||
Configuration.exportTimeout.set(timeout);
|
||||
} catch (NumberFormatException nex) {
|
||||
System.err.println("Bad timeout value");
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseExportFileTimeout(Queue<String> args) {
|
||||
if (args.isEmpty()) {
|
||||
System.err.println("timeout parameter expected");
|
||||
badArguments();
|
||||
}
|
||||
try {
|
||||
int timeout = Integer.parseInt(args.remove());
|
||||
Configuration.decompilationTimeoutFile.set(timeout);
|
||||
} catch (NumberFormatException nex) {
|
||||
System.err.println("Bad timeout value");
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseAffinity(Queue<String> args) {
|
||||
if (Platform.isWindows()) {
|
||||
if (args.isEmpty()) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.jpexs.decompiler.flash.console.ContextMenuTools;
|
||||
import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel;
|
||||
import com.jpexs.decompiler.flash.gui.proxy.ProxyFrame;
|
||||
import com.jpexs.helpers.Cache;
|
||||
import com.jpexs.helpers.CancellableWorker;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import com.jpexs.helpers.ProgressListener;
|
||||
import com.jpexs.helpers.streams.SeekableInputStream;
|
||||
@@ -151,17 +152,17 @@ public class Main {
|
||||
startWork(name, percent, null);
|
||||
}
|
||||
|
||||
public static void startWork(String name, Runnable cancelCallback) {
|
||||
startWork(name, -1, cancelCallback);
|
||||
public static void startWork(String name, CancellableWorker worker) {
|
||||
startWork(name, -1, worker);
|
||||
}
|
||||
|
||||
public static void startWork(final String name, final int percent, final Runnable cancelCallback) {
|
||||
public static void startWork(final String name, final int percent, final CancellableWorker worker) {
|
||||
working = true;
|
||||
View.execInEventDispatchLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mainFrame != null) {
|
||||
mainFrame.setWorkStatus(name, cancelCallback);
|
||||
mainFrame.setWorkStatus(name, worker);
|
||||
if (percent == -1) {
|
||||
mainFrame.hidePercent();
|
||||
} else {
|
||||
@@ -186,12 +187,17 @@ public class Main {
|
||||
|
||||
public static void stopWork() {
|
||||
working = false;
|
||||
if (mainFrame != null) {
|
||||
mainFrame.setWorkStatus("", null);
|
||||
}
|
||||
if (loadingDialog != null) {
|
||||
loadingDialog.setDetail("");
|
||||
}
|
||||
View.execInEventDispatchLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mainFrame != null) {
|
||||
mainFrame.setWorkStatus("", null);
|
||||
}
|
||||
if (loadingDialog != null) {
|
||||
loadingDialog.setDetail("");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static SWF parseSWF(String file) throws Exception {
|
||||
@@ -302,6 +308,7 @@ public class Main {
|
||||
}
|
||||
|
||||
public static boolean reloadSWF() {
|
||||
CancellableWorker.cancelBackgroundThreads();
|
||||
if (Main.inputStream == null) {
|
||||
mainFrame.setVisible(false);
|
||||
Helper.emptyObject(mainFrame);
|
||||
|
||||
@@ -101,9 +101,8 @@ import com.jpexs.decompiler.flash.types.RECT;
|
||||
import com.jpexs.decompiler.flash.types.RGB;
|
||||
import com.jpexs.decompiler.flash.types.TEXTRECORD;
|
||||
import com.jpexs.decompiler.graph.ExportMode;
|
||||
import com.jpexs.helpers.AsyncResult;
|
||||
import com.jpexs.helpers.Cache;
|
||||
import com.jpexs.helpers.Callback;
|
||||
import com.jpexs.helpers.CancellableWorker;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import com.jpexs.process.ProcessTools;
|
||||
import java.awt.BorderLayout;
|
||||
@@ -167,8 +166,7 @@ import java.util.Stack;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.Box;
|
||||
@@ -198,7 +196,6 @@ import javax.swing.JTextField;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.border.BevelBorder;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
@@ -247,7 +244,7 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
public JLabel statusLabel = new JLabel("");
|
||||
public JPanel statusPanel = new JPanel();
|
||||
public JButton cancelButton = new JButton();
|
||||
public Runnable cancelButtonPressed;
|
||||
public CancellableWorker currentWorker;
|
||||
public JProgressBar progressBar = new JProgressBar(0, 100);
|
||||
private DeobfuscationDialog deobfuscationDialog;
|
||||
public JTree tagTree;
|
||||
@@ -310,7 +307,7 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
public static final String INTERNAL_VIEWER_CARD = "INTERNALVIEWER";
|
||||
private Map<Integer, String> sourceFontsMap = new HashMap<>();
|
||||
private AbortRetryIgnoreHandler errorHandler = new GuiAbortRetryIgnoreHandler();
|
||||
private FutureTask<Void> setSourceTask;
|
||||
private CancellableWorker setSourceWorker;
|
||||
|
||||
public void setPercent(int percent) {
|
||||
progressBar.setValue(percent);
|
||||
@@ -346,15 +343,15 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
statusLabel.setText(s);
|
||||
}
|
||||
|
||||
public void setWorkStatus(String s, Runnable cancelCallback) {
|
||||
public void setWorkStatus(String s, CancellableWorker worker) {
|
||||
if (s.isEmpty()) {
|
||||
loadingPanel.setVisible(false);
|
||||
} else {
|
||||
loadingPanel.setVisible(true);
|
||||
}
|
||||
statusLabel.setText(s);
|
||||
cancelButtonPressed = cancelCallback;
|
||||
cancelButton.setVisible(cancelCallback != null);
|
||||
currentWorker = worker;
|
||||
cancelButton.setVisible(worker != null);
|
||||
}
|
||||
|
||||
private String fixCommandTitle(String title) {
|
||||
@@ -564,7 +561,7 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
|
||||
RibbonApplicationMenu mainMenu = new RibbonApplicationMenu();
|
||||
RibbonApplicationMenuEntryPrimary exportFlaMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("exportfla32"), translate("menu.file.export.fla"), new ActionRedirector(this, "EXPORTFLA"), CommandButtonKind.ACTION_ONLY);
|
||||
RibbonApplicationMenuEntryPrimary exportAllMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("export32"), translate("menu.file.export.all"), new ActionRedirector(this, "EXPORTSEL"), CommandButtonKind.ACTION_ONLY);
|
||||
RibbonApplicationMenuEntryPrimary exportAllMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("export32"), translate("menu.file.export.all"), new ActionRedirector(this, "EXPORT"), CommandButtonKind.ACTION_ONLY);
|
||||
RibbonApplicationMenuEntryPrimary exportSelMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("exportsel32"), translate("menu.file.export.selection"), new ActionRedirector(this, "EXPORTSEL"), CommandButtonKind.ACTION_ONLY);
|
||||
RibbonApplicationMenuEntryPrimary checkUpdatesMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("update32"), translate("menu.help.checkupdates"), new ActionRedirector(this, "CHECKUPDATES"), CommandButtonKind.ACTION_ONLY);
|
||||
RibbonApplicationMenuEntryPrimary aboutMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("about32"), translate("menu.help.about"), new ActionRedirector(this, "ABOUT"), CommandButtonKind.ACTION_ONLY);
|
||||
@@ -1111,8 +1108,8 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (cancelButtonPressed != null) {
|
||||
cancelButtonPressed.run();
|
||||
if (currentWorker != null) {
|
||||
currentWorker.cancel(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -2463,9 +2460,9 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
final String txt = searchDialog.searchField.getText();
|
||||
if (!txt.isEmpty()) {
|
||||
if (abcPanel != null) {
|
||||
(new Thread() {
|
||||
new CancellableWorker() {
|
||||
@Override
|
||||
public void run() {
|
||||
protected Void doInBackground() throws Exception {
|
||||
if (abcPanel.search(txt, searchDialog.ignoreCaseCheckBox.isSelected(), searchDialog.regexpCheckBox.isSelected())) {
|
||||
View.execInEventDispatch(new Runnable() {
|
||||
@Override
|
||||
@@ -2477,12 +2474,13 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
} else {
|
||||
View.showMessageDialog(null, translate("message.search.notfound").replace("%searchtext%", txt), translate("message.search.notfound.title"), JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}).start();
|
||||
}.execute();
|
||||
} else {
|
||||
(new Thread() {
|
||||
new CancellableWorker() {
|
||||
@Override
|
||||
public void run() {
|
||||
protected Void doInBackground() {
|
||||
if (actionPanel.search(txt, searchDialog.ignoreCaseCheckBox.isSelected(), searchDialog.regexpCheckBox.isSelected())) {
|
||||
View.execInEventDispatch(new Runnable() {
|
||||
@Override
|
||||
@@ -2493,8 +2491,9 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
} else {
|
||||
View.showMessageDialog(null, translate("message.search.notfound").replace("%searchtext%", txt), translate("message.search.notfound.title"), JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}).start();
|
||||
}.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2646,14 +2645,19 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
if (swf.fileAttributes.actionScript3) {
|
||||
final int multiName = abcPanel.decompiledTextArea.getMultinameUnderCursor();
|
||||
if (multiName > 0) {
|
||||
(new Thread() {
|
||||
new CancellableWorker() {
|
||||
@Override
|
||||
public void run() {
|
||||
public Void doInBackground() throws Exception {
|
||||
Main.startWork(translate("work.renaming") + "...");
|
||||
renameMultiname(multiName);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
Main.stopWork();
|
||||
}
|
||||
}).start();
|
||||
}.execute();
|
||||
|
||||
} else {
|
||||
View.showMessageDialog(null, translate("message.rename.notfound.multiname"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE);
|
||||
@@ -2661,18 +2665,23 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
} else {
|
||||
final String identifier = actionPanel.getStringUnderCursor();
|
||||
if (identifier != null) {
|
||||
(new Thread() {
|
||||
new CancellableWorker() {
|
||||
@Override
|
||||
public void run() {
|
||||
public Void doInBackground() throws Exception {
|
||||
Main.startWork(translate("work.renaming") + "...");
|
||||
try {
|
||||
renameIdentifier(identifier);
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
Main.stopWork();
|
||||
}
|
||||
}).start();
|
||||
}.execute();
|
||||
} else {
|
||||
View.showMessageDialog(null, translate("message.rename.notfound.identifier"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
@@ -2760,9 +2769,9 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
}
|
||||
}
|
||||
final File selfile = sf;
|
||||
(new Thread() {
|
||||
new CancellableWorker() {
|
||||
@Override
|
||||
public void run() {
|
||||
protected Void doInBackground() throws Exception {
|
||||
Helper.freeMem();
|
||||
try {
|
||||
if (compressed) {
|
||||
@@ -2774,9 +2783,14 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage(), translate("error"), JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
Helper.freeMem();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
Main.stopWork();
|
||||
}
|
||||
}).start();
|
||||
}.execute();
|
||||
}
|
||||
break;
|
||||
case "EXPORTSEL":
|
||||
@@ -2798,9 +2812,9 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
final boolean isMp3OrWav = export.getOption(ExportDialog.OPTION_SOUNDS) == 0;
|
||||
final boolean isFormatted = export.getOption(ExportDialog.OPTION_TEXTS) == 1;
|
||||
final boolean onlySel = e.getActionCommand().endsWith("SEL");
|
||||
(new Thread() {
|
||||
new CancellableWorker() {
|
||||
@Override
|
||||
public void run() {
|
||||
public Void doInBackground() throws Exception {
|
||||
try {
|
||||
if (onlySel) {
|
||||
exportSelection(errorHandler, selFile, export);
|
||||
@@ -2817,13 +2831,24 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, "Error during export", ex);
|
||||
View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
Main.stopWork();
|
||||
long timeAfter = System.currentTimeMillis();
|
||||
long timeMs = timeAfter - timeBefore;
|
||||
final long timeMs = timeAfter - timeBefore;
|
||||
|
||||
setStatus(translate("export.finishedin").replace("%time%", Helper.formatTimeSec(timeMs)));
|
||||
View.execInEventDispatchLater(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
setStatus(translate("export.finishedin").replace("%time%", Helper.formatTimeSec(timeMs)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}.execute();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2868,7 +2893,7 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
Main.startWork(translate("work.restoringControlFlow"));
|
||||
final boolean all = e.getActionCommand().endsWith("ALL");
|
||||
if ((!all) || confirmExperimental()) {
|
||||
new SwingWorker() {
|
||||
new CancellableWorker() {
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
int cnt = 0;
|
||||
@@ -2883,12 +2908,23 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
}
|
||||
abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc, abcPanel.decompiledTextArea.getCurrentTrait());
|
||||
}
|
||||
Main.stopWork();
|
||||
View.showMessageDialog(null, "Control flow restored");
|
||||
abcPanel.reload();
|
||||
doFilter();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
Main.stopWork();
|
||||
View.showMessageDialog(null, translate("work.restoringControlFlow.complete"));
|
||||
|
||||
View.execInEventDispatch(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
abcPanel.reload();
|
||||
doFilter();
|
||||
}
|
||||
});
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
break;
|
||||
@@ -2897,29 +2933,39 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
final RenameType renameType = new RenameDialog().display();
|
||||
if (renameType != null) {
|
||||
Main.startWork(translate("work.renaming.identifiers") + "...");
|
||||
new SwingWorker() {
|
||||
new CancellableWorker<Integer>() {
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
try {
|
||||
int cnt = swf.deobfuscateIdentifiers(renameType);
|
||||
Main.stopWork();
|
||||
View.showMessageDialog(null, translate("message.rename.renamed").replace("%count%", "" + cnt));
|
||||
swf.assignClassesToSymbols();
|
||||
clearCache();
|
||||
if (abcPanel != null) {
|
||||
abcPanel.reload();
|
||||
protected Integer doInBackground() throws Exception {
|
||||
int cnt = swf.deobfuscateIdentifiers(renameType);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
View.execInEventDispatch(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
int cnt = get();
|
||||
Main.stopWork();
|
||||
View.showMessageDialog(null, translate("message.rename.renamed").replace("%count%", "" + cnt));
|
||||
swf.assignClassesToSymbols();
|
||||
clearCache();
|
||||
if (abcPanel != null) {
|
||||
abcPanel.reload();
|
||||
}
|
||||
doFilter();
|
||||
reload(true);
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, "Error during renaming identifiers", ex);
|
||||
Main.stopWork();
|
||||
View.showMessageDialog(null, translate("error.occured").replace("%error%", ex.getClass().getSimpleName()));
|
||||
}
|
||||
}
|
||||
doFilter();
|
||||
reload(true);
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, "Error during renaming identifiers", ex);
|
||||
Main.stopWork();
|
||||
View.showMessageDialog(null, translate("error.occured").replace("%error%", ex.getClass().getSimpleName()));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}.execute();
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -2931,7 +2977,7 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
deobfuscationDialog.setVisible(true);
|
||||
if (deobfuscationDialog.ok) {
|
||||
Main.startWork(translate("work.deobfuscating") + "...");
|
||||
new SwingWorker() {
|
||||
new CancellableWorker() {
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
try {
|
||||
@@ -2964,12 +3010,23 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, "Deobfuscation error", ex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
Main.stopWork();
|
||||
View.showMessageDialog(null, translate("work.deobfuscating.complete"));
|
||||
clearCache();
|
||||
abcPanel.reload();
|
||||
doFilter();
|
||||
return true;
|
||||
|
||||
View.execInEventDispatch(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
clearCache();
|
||||
abcPanel.reload();
|
||||
doFilter();
|
||||
}
|
||||
});
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
@@ -3076,22 +3133,14 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
oldValue = tagObj;
|
||||
if (tagObj instanceof ScriptPack) {
|
||||
final ScriptPack scriptLeaf = (ScriptPack) tagObj;
|
||||
if (setSourceWorker != null) {
|
||||
setSourceWorker.cancel(true);
|
||||
}
|
||||
if (!Main.isWorking()) {
|
||||
Main.startWork(translate("work.decompiling") + "...", new Runnable() {
|
||||
CancellableWorker worker = new CancellableWorker() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (setSourceTask != null) {
|
||||
setSourceTask.cancel(true);
|
||||
}
|
||||
abcPanel.decompiledTextArea.setText("//" + AppStrings.translate("work.canceled"));
|
||||
}
|
||||
});
|
||||
|
||||
FutureTask<Void> task = Helper.callAsync(new Callable<Void>() {
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
protected Void doInBackground() throws Exception {
|
||||
int classIndex = -1;
|
||||
for (Trait t : scriptLeaf.abc.script_info[scriptLeaf.scriptIndex].traits.traits) {
|
||||
if (t instanceof TraitClass) {
|
||||
@@ -3108,18 +3157,29 @@ public final class MainFrame extends AppRibbonFrame implements ActionListener, T
|
||||
abcPanel.decompiledTextArea.setNoTrait();
|
||||
return null;
|
||||
}
|
||||
}, new Callback<AsyncResult<Void>>() {
|
||||
|
||||
@Override
|
||||
public void call(AsyncResult<Void> result) {
|
||||
setSourceTask = null;
|
||||
if (result.error != null) {
|
||||
abcPanel.decompiledTextArea.setText("//Decompilation error: " + result.error);
|
||||
}
|
||||
protected void done() {
|
||||
Main.stopWork();
|
||||
|
||||
View.execInEventDispatch(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
get();
|
||||
} catch (CancellationException ex) {
|
||||
abcPanel.decompiledTextArea.setText("//" + AppStrings.translate("work.canceled"));
|
||||
} catch (Exception ex) {
|
||||
abcPanel.decompiledTextArea.setText("//Decompilation error: " + ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
setSourceTask = task;
|
||||
};
|
||||
worker.execute();
|
||||
setSourceWorker = worker;
|
||||
Main.startWork(translate("work.decompiling") + "...", worker);
|
||||
}
|
||||
|
||||
showDetail(DETAILCARDAS3NAVIGATOR);
|
||||
|
||||
@@ -220,7 +220,8 @@ public class View {
|
||||
} else {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(r);
|
||||
} catch (InterruptedException | InvocationTargetException ex) {
|
||||
} catch (InterruptedException ex) {
|
||||
} catch (InvocationTargetException ex) {
|
||||
Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ import com.jpexs.decompiler.flash.gui.abc.tablemodels.UIntTableModel;
|
||||
import com.jpexs.decompiler.flash.helpers.Freed;
|
||||
import com.jpexs.decompiler.flash.helpers.collections.MyEntry;
|
||||
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
|
||||
import com.jpexs.helpers.CancellableWorker;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
@@ -64,10 +65,7 @@ import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -131,21 +129,11 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener, Fr
|
||||
decAdd = ", " + AppStrings.translate("work.decompiling");
|
||||
}
|
||||
|
||||
final AtomicReference<FutureTask<Void>> taskRef = new AtomicReference<>();
|
||||
Main.startWork(workText + " \"" + txt + "\"" + decAdd + " - (" + pos + "/" + allpacks.size() + ") " + item.key.toString() + "... ", new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
FutureTask task = taskRef.get();
|
||||
if (task != null) {
|
||||
task.cancel(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
try {
|
||||
Helper.cancellableCall(new Callable<Void>() {
|
||||
CancellableWorker worker = new CancellableWorker() {
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
public Void doInBackground() throws Exception {
|
||||
decompiledTextArea.cacheScriptPack(item.value, list);
|
||||
if (pat.matcher(decompiledTextArea.getCachedText(item.value)).find()) {
|
||||
found.add(item.value);
|
||||
@@ -153,7 +141,10 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener, Fr
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, taskRef);
|
||||
};
|
||||
worker.execute();
|
||||
Main.startWork(workText + " \"" + txt + "\"" + decAdd + " - (" + pos + "/" + allpacks.size() + ") " + item.key.toString() + "... ", worker);
|
||||
worker.get();
|
||||
} catch (InterruptedException ex) {
|
||||
break;
|
||||
} catch (ExecutionException ex) {
|
||||
|
||||
@@ -223,6 +223,7 @@ public class ASMSourceEditorPane extends LineMarkedEditorPane implements CaretLi
|
||||
abc.bodies[bodyIndex].code = acode;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
} catch (InterruptedException ex) {
|
||||
} catch (ParseException ex) {
|
||||
View.showMessageDialog(this, (ex.text + " on line " + ex.line));
|
||||
selectLine((int) ex.line);
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.jpexs.decompiler.flash.gui.Main;
|
||||
import com.jpexs.decompiler.flash.gui.View;
|
||||
import com.jpexs.decompiler.flash.helpers.collections.MyEntry;
|
||||
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
|
||||
import com.jpexs.helpers.CancellableWorker;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.JTree;
|
||||
@@ -127,9 +128,9 @@ public final class ClassesListTree extends JTree implements TreeSelectionListene
|
||||
final ScriptPack scriptLeaf = (ScriptPack) item;
|
||||
if (!Main.isWorking()) {
|
||||
Main.startWork(AppStrings.translate("work.decompiling") + "...");
|
||||
(new Thread() {
|
||||
new CancellableWorker() {
|
||||
@Override
|
||||
public void run() {
|
||||
public Void doInBackground() throws Exception {
|
||||
int classIndex = -1;
|
||||
for (Trait t : scriptLeaf.abc.script_info[scriptLeaf.scriptIndex].traits.traits) {
|
||||
if (t instanceof TraitClass) {
|
||||
@@ -144,9 +145,14 @@ public final class ClassesListTree extends JTree implements TreeSelectionListene
|
||||
abcPanel.decompiledTextArea.setClassIndex(classIndex);
|
||||
abcPanel.decompiledTextArea.setNoTrait();
|
||||
abcPanel.detailPanel.methodTraitPanel.methodCodePanel.clear();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
Main.stopWork();
|
||||
}
|
||||
}).start();
|
||||
}.execute();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,8 +38,6 @@ 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 javax.swing.SwingUtilities;
|
||||
import javax.swing.event.CaretEvent;
|
||||
import javax.swing.event.CaretListener;
|
||||
@@ -488,7 +486,6 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL
|
||||
cacheScriptPack(scriptLeaf, abcList);
|
||||
cd = getCached(scriptLeaf);
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(DecompiledEditorPane.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
if (cd != null) {
|
||||
final String hilightedCode = cd.text;
|
||||
|
||||
@@ -41,9 +41,8 @@ import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.decompiler.flash.tags.base.ASMSource;
|
||||
import com.jpexs.decompiler.graph.ExportMode;
|
||||
import com.jpexs.decompiler.graph.GraphTargetItem;
|
||||
import com.jpexs.helpers.AsyncResult;
|
||||
import com.jpexs.helpers.Cache;
|
||||
import com.jpexs.helpers.Callback;
|
||||
import com.jpexs.helpers.CancellableWorker;
|
||||
import com.jpexs.helpers.Helper;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
@@ -59,8 +58,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -119,7 +117,7 @@ public class ActionPanel extends JPanel implements ActionListener {
|
||||
private boolean searchIgnoreCase;
|
||||
private boolean searchRegexp;
|
||||
private Cache cache = Cache.getInstance(true);
|
||||
private FutureTask<Void> setSourceTask;
|
||||
private CancellableWorker setSourceWorker;
|
||||
|
||||
public void clearCache() {
|
||||
cache.clear();
|
||||
@@ -287,7 +285,11 @@ public class ActionPanel extends JPanel implements ActionListener {
|
||||
DisassemblyListener listener = getDisassemblyListener();
|
||||
asm.addDisassemblyListener(listener);
|
||||
HilightedTextWriter writer = new HilightedTextWriter(true);
|
||||
asm.getASMSource(SWF.DEFAULT_VERSION, exportMode, writer, lastCode);
|
||||
try {
|
||||
asm.getASMSource(SWF.DEFAULT_VERSION, exportMode, writer, lastCode);
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(ActionPanel.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
asm.removeDisassemblyListener(listener);
|
||||
return new HilightedText(writer);
|
||||
}
|
||||
@@ -339,27 +341,17 @@ public class ActionPanel extends JPanel implements ActionListener {
|
||||
}
|
||||
|
||||
public void setSource(final ASMSource src, final boolean useCache) {
|
||||
if (setSourceTask != null) {
|
||||
setSourceTask.cancel(true);
|
||||
if (setSourceWorker != null) {
|
||||
setSourceWorker.cancel(true);
|
||||
}
|
||||
|
||||
this.src = src;
|
||||
Main.startWork(AppStrings.translate("work.decompiling") + "...", new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (setSourceTask != null) {
|
||||
setSourceTask.cancel(true);
|
||||
}
|
||||
editor.setText("; " + AppStrings.translate("work.canceled"));
|
||||
}
|
||||
});
|
||||
final ASMSource asm = (ASMSource) src;
|
||||
|
||||
FutureTask<Void> task = Helper.callAsync(new Callable<Void>() {
|
||||
CancellableWorker worker = new CancellableWorker() {
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
protected Void doInBackground() throws Exception {
|
||||
editor.setText("; " + AppStrings.translate("work.disassembling") + "...");
|
||||
if (Configuration.decompile.get()) {
|
||||
decompiledEditor.setText("//" + AppStrings.translate("work.waitingfordissasembly") + "...");
|
||||
@@ -389,20 +381,30 @@ public class ActionPanel extends JPanel implements ActionListener {
|
||||
setDecompiledEditMode(false);
|
||||
return null;
|
||||
}
|
||||
}, new Callback<AsyncResult<Void>>() {
|
||||
|
||||
@Override
|
||||
public void call(AsyncResult<Void> result) {
|
||||
setSourceTask = null;
|
||||
if (result.error != null) {
|
||||
decompiledEditor.setText("//Decompilation error: " + result.error);
|
||||
}
|
||||
protected void done() {
|
||||
setSourceWorker = null;
|
||||
Main.stopWork();
|
||||
|
||||
View.execInEventDispatch(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
get();
|
||||
} catch (CancellationException ex) {
|
||||
editor.setText("; " + AppStrings.translate("work.canceled"));
|
||||
} catch (Exception ex) {
|
||||
decompiledEditor.setText("//Decompilation error: " + ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
setSourceTask = task;
|
||||
};
|
||||
worker.execute();
|
||||
setSourceWorker = worker;
|
||||
Main.startWork(AppStrings.translate("work.decompiling") + "...", worker);
|
||||
}
|
||||
|
||||
public void hilightOffset(long offset) {
|
||||
|
||||
@@ -377,4 +377,5 @@ menu.advancedsettings.advancedsettings = Advanced Settings
|
||||
menu.recentFiles = Recent files
|
||||
|
||||
#after version 1.7.4
|
||||
work.restoringControlFlow.complete = Control flow restored
|
||||
|
||||
|
||||
@@ -375,3 +375,6 @@ work.canceled = Megszak\u00edtva
|
||||
work.restoringControlFlow = Vez\u00e9rl\u00e9si-folyam helyre\u00e1ll\u00edt\u00e1s
|
||||
menu.advancedsettings.advancedsettings = Halad\u00f3 be\u00e1ll\u00edt\u00e1sok
|
||||
menu.recentFiles = El\u0151zm\u00e9nyek
|
||||
|
||||
#after version 1.7.4
|
||||
work.restoringControlFlow.complete = Vez\u00e9rl\u00e9si-folyam helyre\u00e1ll\u00edt\u00e1s befejez\u0151d\u00f6tt
|
||||
|
||||
@@ -139,7 +139,7 @@ public class DefineButtonTag extends CharacterTag implements ASMSource, BoundedT
|
||||
* @return ASM source
|
||||
*/
|
||||
@Override
|
||||
public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List<Action> actions) {
|
||||
public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List<Action> actions) throws InterruptedException {
|
||||
if (actions == null) {
|
||||
actions = getActions(version);
|
||||
}
|
||||
@@ -163,7 +163,7 @@ public class DefineButtonTag extends CharacterTag implements ASMSource, BoundedT
|
||||
* @return List of actions
|
||||
*/
|
||||
@Override
|
||||
public List<Action> getActions(int version) {
|
||||
public List<Action> getActions(int version) throws InterruptedException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int prevLength = 0;
|
||||
@@ -176,8 +176,10 @@ public class DefineButtonTag extends CharacterTag implements ASMSource, BoundedT
|
||||
MemoryInputStream rri = new MemoryInputStream(baos.toByteArray());
|
||||
rri.seek(prevLength);
|
||||
|
||||
List<Action> list = ActionListReader.readActionList(listeners, getPos() + hdrSize - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/);
|
||||
List<Action> list = ActionListReader.readActionListTimeout(listeners, getPos() + hdrSize - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/);
|
||||
return list;
|
||||
} catch (InterruptedException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(DoActionTag.class.getName()).log(Level.SEVERE, null, ex);
|
||||
return new ArrayList<>();
|
||||
|
||||
@@ -192,7 +192,7 @@ public class DefineSpriteTag extends CharacterTag implements Container, BoundedT
|
||||
* @param skipUnusualTags
|
||||
* @throws IOException
|
||||
*/
|
||||
public DefineSpriteTag(SWF swf, byte[] data, int version, int level, long pos, boolean parallel, boolean skipUnusualTags) throws IOException {
|
||||
public DefineSpriteTag(SWF swf, byte[] data, int version, int level, long pos, boolean parallel, boolean skipUnusualTags) throws IOException, InterruptedException {
|
||||
super(swf, ID, "DefineSprite", data, pos);
|
||||
SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), version, pos);
|
||||
spriteId = sis.readUI16();
|
||||
|
||||
@@ -76,7 +76,7 @@ public class DoActionTag extends Tag implements ASMSource {
|
||||
* @return ASM source
|
||||
*/
|
||||
@Override
|
||||
public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List<Action> actions) {
|
||||
public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List<Action> actions) throws InterruptedException {
|
||||
if (actions == null) {
|
||||
actions = getActions(version);
|
||||
}
|
||||
@@ -104,7 +104,7 @@ public class DoActionTag extends Tag implements ASMSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> getActions(int version) {
|
||||
public List<Action> getActions(int version) throws InterruptedException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int prevLength = 0;
|
||||
@@ -119,8 +119,10 @@ public class DoActionTag extends Tag implements ASMSource {
|
||||
baos.write(actionBytes);
|
||||
MemoryInputStream rri = new MemoryInputStream(baos.toByteArray());
|
||||
rri.seek(prevLength);
|
||||
List<Action> list = ActionListReader.readActionList(listeners, getPos() - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/);
|
||||
List<Action> list = ActionListReader.readActionListTimeout(listeners, getPos() - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/);
|
||||
return list;
|
||||
} catch (InterruptedException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(DoActionTag.class.getName()).log(Level.SEVERE, null, ex);
|
||||
return new ArrayList<>();
|
||||
|
||||
@@ -103,7 +103,7 @@ public class DoInitActionTag extends CharacterIdTag implements ASMSource {
|
||||
* @return ASM source
|
||||
*/
|
||||
@Override
|
||||
public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List<Action> actions) {
|
||||
public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List<Action> actions) throws InterruptedException {
|
||||
if (actions == null) {
|
||||
actions = getActions(version);
|
||||
}
|
||||
@@ -111,7 +111,7 @@ public class DoInitActionTag extends CharacterIdTag implements ASMSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> getActions(int version) {
|
||||
public List<Action> getActions(int version) throws InterruptedException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int prevLength = 0;
|
||||
@@ -129,8 +129,10 @@ public class DoInitActionTag extends CharacterIdTag implements ASMSource {
|
||||
baos.write(actionBytes);
|
||||
MemoryInputStream rri = new MemoryInputStream(baos.toByteArray());
|
||||
rri.seek(prevLength);
|
||||
List<Action> list = ActionListReader.readActionList(listeners, getPos() + 2 - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/);
|
||||
List<Action> list = ActionListReader.readActionListTimeout(listeners, getPos() + 2 - prevLength, rri, version, prevLength, -1, toString()/*FIXME?*/);
|
||||
return list;
|
||||
} catch (InterruptedException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(DoActionTag.class.getName()).log(Level.SEVERE, null, ex);
|
||||
return new ArrayList<>();
|
||||
|
||||
@@ -36,7 +36,7 @@ public interface ASMSource {
|
||||
* @param exportMode PCode or hex?
|
||||
* @return ASM source
|
||||
*/
|
||||
public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List<Action> actions);
|
||||
public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List<Action> actions) throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Whether or not this object contains ASM source
|
||||
@@ -51,7 +51,7 @@ public interface ASMSource {
|
||||
* @param version Version
|
||||
* @return List of actions
|
||||
*/
|
||||
public List<Action> getActions(int version);
|
||||
public List<Action> getActions(int version) throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Sets actions associated with this object
|
||||
|
||||
@@ -148,7 +148,7 @@ public class BUTTONCONDACTION implements ASMSource, Exportable, ContainerItem {
|
||||
* @return ASM source
|
||||
*/
|
||||
@Override
|
||||
public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List<Action> actions) {
|
||||
public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List<Action> actions) throws InterruptedException {
|
||||
if (actions == null) {
|
||||
actions = getActions(version);
|
||||
}
|
||||
@@ -172,11 +172,13 @@ public class BUTTONCONDACTION implements ASMSource, Exportable, ContainerItem {
|
||||
* @return List of actions
|
||||
*/
|
||||
@Override
|
||||
public List<Action> getActions(int version) {
|
||||
public List<Action> getActions(int version) throws InterruptedException {
|
||||
try {
|
||||
List<Action> list = ActionListReader.readActionList(listeners, getPos() + 4, new MemoryInputStream(actionBytes), version, 0, -1, toString()/*FIXME?*/);
|
||||
List<Action> list = ActionListReader.readActionListTimeout(listeners, getPos() + 4, new MemoryInputStream(actionBytes), version, 0, -1, toString()/*FIXME?*/);
|
||||
return list;
|
||||
|
||||
} catch (InterruptedException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(BUTTONCONDACTION.class.getName()).log(Level.SEVERE, null, ex);
|
||||
return new ArrayList<>();
|
||||
|
||||
@@ -153,7 +153,7 @@ public class CLIPACTIONRECORD implements ASMSource, Exportable, ContainerItem {
|
||||
* @return ASM source
|
||||
*/
|
||||
@Override
|
||||
public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List<Action> actions) {
|
||||
public GraphTextWriter getASMSource(int version, ExportMode exportMode, GraphTextWriter writer, List<Action> actions) throws InterruptedException {
|
||||
if (actions == null) {
|
||||
actions = getActions(version);
|
||||
}
|
||||
@@ -171,10 +171,12 @@ public class CLIPACTIONRECORD implements ASMSource, Exportable, ContainerItem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> getActions(int version) {
|
||||
public List<Action> getActions(int version) throws InterruptedException {
|
||||
try {
|
||||
List<Action> list = ActionListReader.readActionList(listeners, getPos() + hdrPos, new MemoryInputStream(actionBytes), version, 0, -1, toString()/*FIXME?*/);
|
||||
List<Action> list = ActionListReader.readActionListTimeout(listeners, getPos() + hdrPos, new MemoryInputStream(actionBytes), version, 0, -1, toString()/*FIXME?*/);
|
||||
return list;
|
||||
} catch (InterruptedException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(BUTTONCONDACTION.class.getName()).log(Level.SEVERE, null, ex);
|
||||
return new ArrayList<>();
|
||||
|
||||
@@ -103,6 +103,10 @@ public class Graph {
|
||||
}
|
||||
|
||||
private boolean fixGraphOnce(List<Object> localData, GraphPart part, List<GraphPart> visited, boolean doChildren) throws InterruptedException {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
|
||||
if (visited.contains(part)) {
|
||||
return false;
|
||||
}
|
||||
@@ -1358,7 +1362,7 @@ public class Graph {
|
||||
}
|
||||
|
||||
protected List<GraphTargetItem> printGraph(List<GraphPart> visited, List<Object> localData, Stack<GraphTargetItem> stack, List<GraphPart> allParts, GraphPart parent, GraphPart part, List<GraphPart> stopPart, List<Loop> loops, List<GraphTargetItem> ret, int staticOperation, String path, int recursionLevel) throws InterruptedException {
|
||||
if (Thread.interrupted()) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
if (stopPart == null) {
|
||||
@@ -2034,7 +2038,7 @@ public class Graph {
|
||||
protected void checkGraph(List<GraphPart> allBlocks) {
|
||||
}
|
||||
|
||||
private List<GraphPart> makeGraph(GraphSource code, List<GraphPart> allBlocks, List<Integer> alternateEntries) {
|
||||
private List<GraphPart> makeGraph(GraphSource code, List<GraphPart> allBlocks, List<Integer> alternateEntries) throws InterruptedException {
|
||||
HashMap<Integer, List<Integer>> refs = code.visitCode(alternateEntries);
|
||||
List<GraphPart> ret = new ArrayList<>();
|
||||
boolean[] visited = new boolean[code.size()];
|
||||
@@ -2052,7 +2056,10 @@ public class Graph {
|
||||
return ip;
|
||||
}
|
||||
|
||||
private GraphPart makeGraph(GraphPart parent, GraphPath path, GraphSource code, int startip, int lastIp, List<GraphPart> allBlocks, HashMap<Integer, List<Integer>> refs, boolean[] visited2) {
|
||||
private GraphPart makeGraph(GraphPart parent, GraphPath path, GraphSource code, int startip, int lastIp, List<GraphPart> allBlocks, HashMap<Integer, List<Integer>> refs, boolean[] visited2) throws InterruptedException {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
|
||||
int ip = startip;
|
||||
for (GraphPart p : allBlocks) {
|
||||
|
||||
@@ -61,7 +61,7 @@ public class GraphPart implements Serializable {
|
||||
}
|
||||
|
||||
private boolean leadsTo(List<Object> localData, Graph gr, GraphSource code, GraphPart part, List<GraphPart> visited, List<Loop> loops) throws InterruptedException {
|
||||
if (Thread.interrupted()) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,10 @@ public abstract class GraphSource implements Serializable {
|
||||
|
||||
public abstract List<GraphTargetItem> translatePart(GraphPart part, List<Object> localData, Stack<GraphTargetItem> stack, int start, int end, int staticOperation, String path) throws InterruptedException;
|
||||
|
||||
private void visitCode(int ip, int lastIp, HashMap<Integer, List<Integer>> refs, int endIp) {
|
||||
private void visitCode(int ip, int lastIp, HashMap<Integer, List<Integer>> refs, int endIp) throws InterruptedException {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
boolean debugMode = false;
|
||||
while (((endIp == -1) || (ip < endIp)) && (ip < size())) {
|
||||
refs.get(ip).add(lastIp);
|
||||
@@ -87,7 +90,7 @@ public abstract class GraphSource implements Serializable {
|
||||
};
|
||||
}
|
||||
|
||||
public HashMap<Integer, List<Integer>> visitCode(List<Integer> alternateEntries) {
|
||||
public HashMap<Integer, List<Integer>> visitCode(List<Integer> alternateEntries) throws InterruptedException {
|
||||
HashMap<Integer, List<Integer>> refs = new HashMap<>();
|
||||
int siz = size();
|
||||
for (int i = 0; i < siz; i++) {
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.jpexs.decompiler.graph.model;
|
||||
|
||||
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
|
||||
import com.jpexs.decompiler.graph.GraphTargetItem;
|
||||
import static com.jpexs.decompiler.graph.GraphTargetItem.NOPRECEDENCE;
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2013 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* 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.helpers;
|
||||
|
||||
/**
|
||||
* Class with helper method
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class AsyncResult<T> {
|
||||
|
||||
public T result;
|
||||
|
||||
public Throwable error;
|
||||
|
||||
public AsyncResult(T result, Throwable error) {
|
||||
this.result = result;
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
@@ -162,8 +162,7 @@ public class Cache {
|
||||
|
||||
cacheFiles.put(key, temp);
|
||||
|
||||
|
||||
} catch (Exception ex) {
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
} else if (storageType == STORAGE_MEMORY) {
|
||||
|
||||
128
trunk/src/com/jpexs/helpers/CancellableWorker.java
Normal file
128
trunk/src/com/jpexs/helpers/CancellableWorker.java
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2013 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* 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.helpers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.RunnableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public abstract class CancellableWorker<T> implements RunnableFuture<T> {
|
||||
|
||||
private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();
|
||||
private static List<CancellableWorker> workers = Collections.synchronizedList(new ArrayList<CancellableWorker>());
|
||||
private final FutureTask<T> future;
|
||||
|
||||
public CancellableWorker() {
|
||||
super();
|
||||
Callable<T> callable =
|
||||
new Callable<T>() {
|
||||
@Override
|
||||
public T call() throws Exception {
|
||||
return doInBackground();
|
||||
}
|
||||
};
|
||||
|
||||
future = new FutureTask<T>(callable) {
|
||||
@Override
|
||||
protected void done() {
|
||||
workerDone();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected abstract T doInBackground() throws Exception;
|
||||
|
||||
@Override
|
||||
public final void run() {
|
||||
workers.add(this);
|
||||
future.run();
|
||||
}
|
||||
|
||||
protected void done() {
|
||||
}
|
||||
|
||||
public final void execute() {
|
||||
THREAD_POOL.execute(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean cancel(boolean mayInterruptIfRunning) {
|
||||
return future.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isCancelled() {
|
||||
return future.isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isDone() {
|
||||
return future.isDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T get() throws InterruptedException, ExecutionException {
|
||||
return future.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T get(long timeout, TimeUnit unit) throws InterruptedException,
|
||||
ExecutionException, TimeoutException {
|
||||
return future.get(timeout, unit);
|
||||
}
|
||||
|
||||
private void workerDone() {
|
||||
workers.remove(this);
|
||||
done();
|
||||
}
|
||||
|
||||
public static <T> T call(final Callable<T> c, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
|
||||
CancellableWorker<T> worker = new CancellableWorker<T>() {
|
||||
|
||||
@Override
|
||||
protected T doInBackground() throws Exception {
|
||||
return c.call();
|
||||
}
|
||||
};
|
||||
try {
|
||||
worker.execute();
|
||||
return worker.get(timeout, timeUnit);
|
||||
} finally {
|
||||
worker.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void cancelBackgroundThreads() {
|
||||
List<CancellableWorker> oldWorkers = workers;
|
||||
workers = new ArrayList<>();
|
||||
for (CancellableWorker worker : oldWorkers) {
|
||||
worker.cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,15 +41,6 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -62,8 +53,6 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class Helper {
|
||||
|
||||
private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();
|
||||
|
||||
/**
|
||||
* Converts array of int values to string
|
||||
*
|
||||
@@ -625,61 +614,6 @@ public class Helper {
|
||||
return data;
|
||||
}
|
||||
|
||||
public static <T> T timedCall(Callable<T> c, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
|
||||
FutureTask<T> task = new FutureTask<>(c);
|
||||
THREAD_POOL.execute(task);
|
||||
try {
|
||||
return task.get(timeout, timeUnit);
|
||||
} finally {
|
||||
task.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> FutureTask<T> callAsync(final Callable<T> c) {
|
||||
return callAsync(c, null);
|
||||
}
|
||||
|
||||
public static <T> FutureTask<T> callAsync(final Callable<T> c, final Callback<AsyncResult<T>> callback) {
|
||||
FutureTask<T> task = new FutureTask<>(new Callable<T>() {
|
||||
|
||||
@Override
|
||||
public T call() throws Exception {
|
||||
try {
|
||||
T t = c.call();
|
||||
if (callback != null) {
|
||||
callback.call(new AsyncResult<>(t, null));
|
||||
}
|
||||
return t;
|
||||
} catch (Throwable ex) {
|
||||
if (callback != null) {
|
||||
callback.call(new AsyncResult<T>(null, ex));
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
});
|
||||
THREAD_POOL.execute(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
public static <T> T cancellableCall(final Callable<T> c, final AtomicReference<FutureTask<T>> cancellation) throws InterruptedException, ExecutionException {
|
||||
FutureTask<T> task = new FutureTask<>(new Callable<T>() {
|
||||
|
||||
@Override
|
||||
public T call() throws Exception {
|
||||
T t = c.call();
|
||||
return t;
|
||||
}
|
||||
});
|
||||
cancellation.set(task);
|
||||
THREAD_POOL.execute(task);
|
||||
try {
|
||||
return task.get();
|
||||
} catch (CancellationException ex) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean contains(int[] array, int value) {
|
||||
for (int i : array) {
|
||||
if (i == value) {
|
||||
|
||||
Reference in New Issue
Block a user