As3ScriptReplacer as interface

Better handling missing directories
Tip for directories in advanced setting refactored - link to Flex SDK
Use Flex SDK switch in Advanced settings
This commit is contained in:
Jindra Petřík
2016-08-16 23:31:12 +02:00
parent 95785ba218
commit c4e1879b79
21 changed files with 316 additions and 154 deletions

View File

@@ -61,9 +61,10 @@ import com.jpexs.decompiler.flash.dumpview.DumpInfo;
import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecial;
import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecialType;
import com.jpexs.decompiler.flash.exporters.script.LinkReportExporter;
import com.jpexs.decompiler.flash.flexsdk.As3ScriptReplacer;
import com.jpexs.decompiler.flash.flexsdk.MxmlcAs3ScriptReplacer;
import com.jpexs.decompiler.flash.flexsdk.MxmlcException;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin;
import com.jpexs.decompiler.flash.importers.As3ScriptReplacerInterface;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.types.annotations.Internal;
@@ -1404,66 +1405,10 @@ public class ABC {
method_info.remove(index);
}
public boolean replaceScriptPack(ScriptPack pack, String as) throws AVM2ParseException, CompilationException, IOException, InterruptedException {
final boolean USE_FLEX = true;
boolean isSimple = pack.isSimple;
if (USE_FLEX) {
if (!pack.isSimple) {
return false;
}
As3ScriptReplacer asr = new As3ScriptReplacer(Configuration.flexSdkLocation.get(), new LinkReportExporter());
try {
asr.replaceScript(pack.getSwf(), pack, as);
} catch (MxmlcException ex) {
throw new AVM2ParseException(ex.getMxmlcErrorOutput(), 0);
}
} else {
String scriptName = pack.getPathScriptName() + ".as";
int oldIndex = pack.scriptIndex;
int newIndex = script_info.size();
String documentClass = getSwf().getDocumentClass();
boolean isDocumentClass = documentClass != null && documentClass.equals(pack.getClassPath().toString());
ScriptInfo si = script_info.get(oldIndex);
if (isSimple) {
si.delete(this, true);
} else {
for (int t : pack.traitIndices) {
si.traits.traits.get(t).delete(this, true);
}
}
int newClassIndex = instance_info.size();
for (int t : pack.traitIndices) {
if (si.traits.traits.get(t) instanceof TraitClass) {
TraitClass tc = (TraitClass) si.traits.traits.get(t);
newClassIndex = tc.class_info + 1;
}
}
List<ABC> otherAbcs = new ArrayList<>(pack.allABCs);
otherAbcs.remove(this);
ActionScript3Parser.compile(as, this, otherAbcs, isDocumentClass, scriptName, newClassIndex, oldIndex);
if (isSimple) {
// Move newly added script to its position
script_info.set(oldIndex, script_info.get(newIndex));
script_info.remove(newIndex);
} else {
script_info.get(newIndex).setModified(true);
//Note: Is deleting traits safe?
List<Integer> todel = new ArrayList<>(new TreeSet<>(pack.traitIndices));
for (int i = todel.size() - 1; i >= 0; i--) {
si.traits.traits.remove((int) todel.get(i));
}
}
script_info.get(oldIndex).setModified(true);
}
public boolean replaceScriptPack(As3ScriptReplacerInterface replacer, ScriptPack pack, String as) throws AVM2ParseException, CompilationException, IOException, InterruptedException {
replacer.replaceScript(pack, as);
((Tag) parentTag).setModified(true);
return !isSimple;
return pack.isSimple;
}
private void packMethods() {

View File

@@ -613,6 +613,10 @@ public class Configuration {
@ConfigurationInternal
public static final ConfigurationItem<Double> guiAvm2DocsSplitPaneDividerLocationPercent = null;
@ConfigurationDefaultBoolean(false)
@ConfigurationCategory("script")
public static final ConfigurationItem<Boolean> useFlexAs3Compiler = null;
private enum OSId {
WINDOWS, OSX, UNIX

View File

@@ -3,6 +3,7 @@ package com.jpexs.decompiler.flash.flexsdk;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.ScriptPack;
import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException;
import com.jpexs.decompiler.flash.abc.types.InstanceInfo;
import com.jpexs.decompiler.flash.abc.types.ScriptInfo;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
@@ -15,6 +16,7 @@ import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings;
import com.jpexs.decompiler.flash.exporters.swf.SwfToSwcExporter;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.graph.CompilationException;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.helpers.Helper;
import java.io.ByteArrayInputStream;
@@ -28,12 +30,13 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.jpexs.decompiler.flash.importers.As3ScriptReplacerInterface;
public class As3ScriptReplacer extends MxmlcRunner {
public class MxmlcAs3ScriptReplacer extends MxmlcRunner implements As3ScriptReplacerInterface {
private LinkReportExporter linkReporter;
public As3ScriptReplacer(String flexSdkPath, LinkReportExporter linkReporter) {
public MxmlcAs3ScriptReplacer(String flexSdkPath, LinkReportExporter linkReporter) {
super(flexSdkPath);
this.linkReporter = linkReporter;
}
@@ -87,8 +90,9 @@ public class As3ScriptReplacer extends MxmlcRunner {
return false;
}
public void replaceScript(SWF swf, ScriptPack oldPack, String txt) throws IOException, MxmlcException, InterruptedException {
if (!oldPack.isSimple) {
@Override
public void replaceScript(ScriptPack pack, String text) throws AVM2ParseException, CompilationException, IOException, InterruptedException {
if (!pack.isSimple) {
throw new IOException("Cannot compile such file"); //Alchemy, etc.
}
@@ -96,23 +100,23 @@ public class As3ScriptReplacer extends MxmlcRunner {
try {
tempDir = Files.createTempDirectory("ffdec-mxmlc-replace").toFile();
File pkgDir = tempDir;
for (String pkgPart : oldPack.getClassPath().packageStr.toList()) {
for (String pkgPart : pack.getClassPath().packageStr.toList()) {
if (!pkgPart.isEmpty()) {
pkgDir = new File(pkgDir, pkgPart);
}
}
pkgDir.mkdirs();
File scriptFileToCompile = new File(pkgDir, oldPack.getClassPath().className + ".as");
File scriptFileToCompile = new File(pkgDir, pack.getClassPath().className + ".as");
File compiledSwfFile = new File(pkgDir, "out.swf");
File swcFile = new File(pkgDir, "out.swc");
//Make copy without the old script
SWF swfCopy = recompileSWF(swf);
SWF swfCopy = recompileSWF(pack.getSwf());
List<ABC> modAbcs = new ArrayList<>();
List<ScriptPack> copyPacks = swfCopy.getAS3Packs();
for (ScriptPack sp : copyPacks) {
if (sp.getClassPath().equals(oldPack.getClassPath())) {
if (sp.getClassPath().equals(pack.getClassPath())) {
sp.abc.script_info.get(sp.scriptIndex).delete(sp.abc, true);
((Tag) sp.abc.parentTag).setModified(true);
modAbcs.add(sp.abc);
@@ -151,18 +155,22 @@ public class As3ScriptReplacer extends MxmlcRunner {
swcExport.exportSwf(swfCopy, swcFile, true);
//Write new script
Helper.writeFile(scriptFileToCompile.getAbsolutePath(), txt.getBytes("UTF-8"));
Helper.writeFile(scriptFileToCompile.getAbsolutePath(), text.getBytes("UTF-8"));
//Compile it (and subclasses stubs)
mxmlc("-strict=false", "-include-inheritance-dependencies-only", "-warnings=false", "-library-path", swcFile.getAbsolutePath(), "-source-path", tempDir.getAbsolutePath(), "-output", compiledSwfFile.getAbsolutePath(), "-debug=true", scriptFileToCompile.getAbsolutePath());
try {
//Compile it (and subclasses stubs)
mxmlc("-strict=false", "-include-inheritance-dependencies-only", "-warnings=false", "-library-path", swcFile.getAbsolutePath(), "-source-path", tempDir.getAbsolutePath(), "-output", compiledSwfFile.getAbsolutePath(), "-debug=true", scriptFileToCompile.getAbsolutePath());
} catch (MxmlcException ex1) {
throw new AVM2ParseException(ex1.getMxmlcErrorOutput(), 0);
}
try (FileInputStream fis = new FileInputStream(compiledSwfFile)) {
SWF newSWF = new SWF(fis, false, false);
List<ABCContainerTag> newTags = newSWF.getAbcList();
int oldScriptIndex = oldPack.scriptIndex;
int oldScriptIndex = pack.scriptIndex;
int oldClassIndex = -1;
ScriptInfo oldScriptInfo = oldPack.abc.script_info.get(oldPack.scriptIndex);
ScriptInfo oldScriptInfo = pack.abc.script_info.get(pack.scriptIndex);
for (Trait t : oldScriptInfo.traits.traits) {
if (t instanceof TraitClass) {
int traitClassIndex = ((TraitClass) t).class_info;
@@ -171,18 +179,18 @@ public class As3ScriptReplacer extends MxmlcRunner {
}
}
}
if (oldPack.isSimple) {
oldScriptInfo.delete(oldPack.abc, true);
if (pack.isSimple) {
oldScriptInfo.delete(pack.abc, true);
} else {
//NOO
}
oldPack.abc.pack(); // removes old classes/methods/scripts
pack.abc.pack(); // removes old classes/methods/scripts
ABCContainerTag newTagsLast = newTags.get(newTags.size() - 1);
ABC newLastAbc = newTagsLast.getABC();
Map<Integer, Integer> classesMap = new HashMap<>();
Map<Integer, Integer> scriptsMap = new HashMap<>();
oldPack.abc.mergeABC(newLastAbc,
pack.abc.mergeABC(newLastAbc,
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
@@ -207,10 +215,10 @@ public class As3ScriptReplacer extends MxmlcRunner {
List<ScriptInfo> addedScripts = new ArrayList<>();
for (int i = addedScriptIndices.size() - 1; i >= 0; i--) {
int newScriptIndex = addedScriptIndices.get(i);
addedScripts.add(0, oldPack.abc.script_info.remove(newScriptIndex));
addedScripts.add(0, pack.abc.script_info.remove(newScriptIndex));
}
for (int i = 0; i < addedScripts.size(); i++) {
oldPack.abc.script_info.add(oldScriptIndex + i, addedScripts.get(i));
pack.abc.script_info.add(oldScriptIndex + i, addedScripts.get(i));
}
//IMPORTANT: Map newly created classes to their position as they
@@ -219,7 +227,7 @@ public class As3ScriptReplacer extends MxmlcRunner {
if (oldClassIndex > -1) {
List<Integer> addedClassIndices = new ArrayList<>(classesMap.values());
Collections.sort(addedClassIndices);
int totalClassCount = oldPack.abc.class_info.size();
int totalClassCount = pack.abc.class_info.size();
Map<Integer, Integer> classesRemap = new HashMap<>();
for (int i = 0; i < addedClassIndices.size(); i++) {
classesRemap.put(addedClassIndices.get(i), oldClassIndex + i);
@@ -236,14 +244,20 @@ public class As3ScriptReplacer extends MxmlcRunner {
}
}
}
oldPack.abc.reorganizeClasses(classesRemap);
pack.abc.reorganizeClasses(classesRemap);
}
((Tag) oldPack.abc.parentTag).setModified(true);
((Tag) pack.abc.parentTag).setModified(true);
}
} finally {
if (tempDir != null && tempDir.exists()) {
//deleteFolder(tempDir);
deleteFolder(tempDir);
}
}
}
@Override
public boolean isAvailable() {
String flexLocation = Configuration.flexSdkLocation.get();
return !(flexLocation.isEmpty() || (!new File(MxmlcRunner.getMxmlcPath(flexLocation)).exists()));
}
}

View File

@@ -15,14 +15,14 @@ public class MxmlcRunner {
this.flexSdkPath = flexSdkPath;
}
public String getMxmlcPath() {
public static String getMxmlcPath(String flexSdkPath) {
boolean isWin = System.getProperty("os.name").toLowerCase().contains("win");
return flexSdkPath + File.separator + "bin" + File.separator + "mxmlc" + (isWin ? ".exe" : "");
}
public void mxmlc(String... arguments) throws MxmlcException, InterruptedException, IOException {
String runArgs[] = new String[arguments.length + 1];
runArgs[0] = getMxmlcPath();
runArgs[0] = getMxmlcPath(flexSdkPath);
System.arraycopy(arguments, 0, runArgs, 1, arguments.length);
System.out.println("" + String.join(" ", runArgs));
Process proc = null;

View File

@@ -36,7 +36,7 @@ public class AS3ScriptImporter {
private static final Logger logger = Logger.getLogger(AS3ScriptImporter.class.getName());
public int importScripts(String scriptsFolder, List<ScriptPack> packs) {
public int importScripts(As3ScriptReplacerInterface scriptReplacer, String scriptsFolder, List<ScriptPack> packs) {
if (!scriptsFolder.endsWith(File.separator)) {
scriptsFolder += File.separator;
}
@@ -50,7 +50,7 @@ public class AS3ScriptImporter {
String txt = Helper.readTextFile(fileName);
try {
pack.abc.replaceScriptPack(pack, txt);
pack.abc.replaceScriptPack(scriptReplacer, pack, txt);
} catch (AVM2ParseException ex) {
logger.log(Level.SEVERE, "%error% on line %line%, file: %file%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)).replace("%file%", fileName));
} catch (CompilationException ex) {

View File

@@ -0,0 +1,24 @@
package com.jpexs.decompiler.flash.importers;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.exporters.script.LinkReportExporter;
import com.jpexs.decompiler.flash.flexsdk.MxmlcAs3ScriptReplacer;
public class As3ScriptReplacerFactory {
public static As3ScriptReplacerInterface createByConfig() {
if (Configuration.useFlexAs3Compiler.get()) {
return createFlex();
} else {
return createFFDec();
}
}
public static As3ScriptReplacerInterface createFlex() {
return new MxmlcAs3ScriptReplacer(Configuration.flexSdkLocation.get(), new LinkReportExporter());
}
public static As3ScriptReplacerInterface createFFDec() {
return new FFDecAs3ScriptReplacer();
}
}

View File

@@ -0,0 +1,13 @@
package com.jpexs.decompiler.flash.importers;
import com.jpexs.decompiler.flash.abc.ScriptPack;
import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException;
import com.jpexs.decompiler.graph.CompilationException;
import java.io.IOException;
public interface As3ScriptReplacerInterface {
public boolean isAvailable();
public void replaceScript(ScriptPack pack, String text) throws AVM2ParseException, CompilationException, IOException, InterruptedException;
}

View File

@@ -0,0 +1,72 @@
package com.jpexs.decompiler.flash.importers;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.ScriptPack;
import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScript3Parser;
import com.jpexs.decompiler.flash.abc.types.ScriptInfo;
import com.jpexs.decompiler.flash.abc.types.traits.TraitClass;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.graph.CompilationException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
import javax.swing.JOptionPane;
public class FFDecAs3ScriptReplacer implements As3ScriptReplacerInterface {
@Override
public void replaceScript(ScriptPack pack, String text) throws AVM2ParseException, CompilationException, IOException, InterruptedException {
ABC abc = pack.abc;
SWF swf = pack.abc.getSwf();
String scriptName = pack.getPathScriptName() + ".as";
int oldIndex = pack.scriptIndex;
int newIndex = abc.script_info.size();
String documentClass = swf.getDocumentClass();
boolean isDocumentClass = documentClass != null && documentClass.equals(pack.getClassPath().toString());
ScriptInfo si = abc.script_info.get(oldIndex);
if (pack.isSimple) {
si.delete(abc, true);
} else {
for (int t : pack.traitIndices) {
si.traits.traits.get(t).delete(abc, true);
}
}
int newClassIndex = abc.instance_info.size();
for (int t : pack.traitIndices) {
if (si.traits.traits.get(t) instanceof TraitClass) {
TraitClass tc = (TraitClass) si.traits.traits.get(t);
newClassIndex = tc.class_info + 1;
}
}
List<ABC> otherAbcs = new ArrayList<>(pack.allABCs);
otherAbcs.remove(abc);
ActionScript3Parser.compile(text, abc, otherAbcs, isDocumentClass, scriptName, newClassIndex, oldIndex);
if (pack.isSimple) {
// Move newly added script to its position
abc.script_info.set(oldIndex, abc.script_info.get(newIndex));
abc.script_info.remove(newIndex);
} else {
abc.script_info.get(newIndex).setModified(true);
//Note: Is deleting traits safe?
List<Integer> todel = new ArrayList<>(new TreeSet<>(pack.traitIndices));
for (int i = todel.size() - 1; i >= 0; i--) {
si.traits.traits.remove((int) todel.get(i));
}
}
abc.script_info.get(oldIndex).setModified(true);
}
@Override
public boolean isAvailable() {
File swc = Configuration.getPlayerSWC();
return !(swc == null || !swc.exists());
}
}