Parsing Flex SDK errors

init swf to flex before editig
This commit is contained in:
Jindra Petřík
2016-08-20 18:23:09 +02:00
parent a8ba726816
commit 5c4e07ec1b
13 changed files with 389 additions and 198 deletions
@@ -14,6 +14,8 @@ import com.jpexs.decompiler.flash.exporters.script.AS3ScriptExporter;
import com.jpexs.decompiler.flash.exporters.script.LinkReportExporter;
import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings;
import com.jpexs.decompiler.flash.exporters.swf.SwfToSwcExporter;
import com.jpexs.decompiler.flash.importers.As3ScriptReplaceException;
import com.jpexs.decompiler.flash.importers.As3ScriptReplaceExceptionItem;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.graph.CompilationException;
@@ -31,14 +33,22 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.jpexs.decompiler.flash.importers.As3ScriptReplacerInterface;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MxmlcAs3ScriptReplacer extends MxmlcRunner implements As3ScriptReplacerInterface {
private LinkReportExporter linkReporter;
private ScriptPack initedPack;
private File tempDir;
private File pkgDir;
private File swcFile;
public MxmlcAs3ScriptReplacer(String flexSdkPath, LinkReportExporter linkReporter) {
public MxmlcAs3ScriptReplacer(String flexSdkPath) {
super(flexSdkPath);
this.linkReporter = linkReporter;
}
private synchronized boolean isInited(ScriptPack pack) {
return tempDir != null && initedPack == pack;
}
private static void deleteFolder(File folder) {
@@ -58,7 +68,7 @@ public class MxmlcAs3ScriptReplacer extends MxmlcRunner implements As3ScriptRepl
private SWF recompileSWF(SWF swf) throws IOException, InterruptedException {
ByteArrayOutputStream swfOrigBaos = new ByteArrayOutputStream();
swf.saveTo(swfOrigBaos);
return new SWF(new ByteArrayInputStream(swfOrigBaos.toByteArray()), false, false);
return new SWF(new ByteArrayInputStream(swfOrigBaos.toByteArray()), Configuration.parallelSpeedUp.get(), false);
}
private boolean isParentDeleted(ABC abc, List<ABC> allAbcs, DottedChain className) {
@@ -91,27 +101,157 @@ public class MxmlcAs3ScriptReplacer extends MxmlcRunner implements As3ScriptRepl
}
@Override
public void replaceScript(ScriptPack pack, String text) throws AVM2ParseException, CompilationException, IOException, InterruptedException {
public synchronized void replaceScript(ScriptPack pack, String text) throws As3ScriptReplaceException, IOException, InterruptedException {
if (!pack.isSimple) {
throw new IOException("Cannot compile such file"); //Alchemy, etc.
throw new IOException("Cannot compile such file"); //Alchemy, etc. TODO: handle better
}
File tempDir = null;
if (!isInited(pack)) {
initReplacement(pack);
}
File scriptFileToCompile = new File(pkgDir, pack.getClassPath().className + ".as");
//Write new script
Helper.writeFile(scriptFileToCompile.getAbsolutePath(), text.getBytes("UTF-8"));
File compiledSwfFile = new File(pkgDir, "out.swf");
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) {
//String compiledFilePath = scriptFileToCompile.getAbsolutePath();
Pattern errPattern = Pattern.compile("^" + Pattern.quote(tempDir.getAbsolutePath()) + "(?<file>.*)\\((?<line>[0-9]+)\\): col: (?<col>[0-9]+) (?<message>.*)$");
String err = ex1.getMxmlcErrorOutput();
String errLines[] = err.split("\r?\n");
List<As3ScriptReplaceExceptionItem> errorItems = new ArrayList<>();
for (int i = 0; i < errLines.length; i++) {
String line = errLines[i].trim();
Matcher m = errPattern.matcher(line);
if (m.matches()) {
String errFile = m.group("file");
int errLine = Integer.parseInt(m.group("line"));
int errCol = Integer.parseInt(m.group("col"));
String errMsg = m.group("message");
errorItems.add(new As3ScriptReplaceExceptionItem(errFile, errMsg, errLine, errCol));
}
}
throw new As3ScriptReplaceException(errorItems);
}
try (FileInputStream fis = new FileInputStream(compiledSwfFile)) {
SWF newSWF = new SWF(fis, false, false);
List<ABCContainerTag> newTags = newSWF.getAbcList();
int oldScriptIndex = pack.scriptIndex;
int oldClassIndex = -1;
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;
if (oldClassIndex == -1 || traitClassIndex < oldClassIndex) {
oldClassIndex = traitClassIndex;
}
}
}
if (pack.isSimple) {
oldScriptInfo.delete(pack.abc, true);
} else {
//NOO
}
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<>();
pack.abc.mergeABC(newLastAbc,
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
classesMap,
new HashMap<>(),
scriptsMap
);
//Reorder newly created scripts to be in place
//where old script was
List<Integer> addedScriptIndices = new ArrayList<>(scriptsMap.values());
Collections.sort(addedScriptIndices);
List<ScriptInfo> addedScripts = new ArrayList<>();
for (int i = addedScriptIndices.size() - 1; i >= 0; i--) {
int newScriptIndex = addedScriptIndices.get(i);
addedScripts.add(0, pack.abc.script_info.remove(newScriptIndex));
}
for (int i = 0; i < addedScripts.size(); i++) {
pack.abc.script_info.add(oldScriptIndex + i, addedScripts.get(i));
}
//IMPORTANT: Map newly created classes to their position as they
//were in original script because FlashPlayer needs
//parent class to be defined earlier
if (oldClassIndex > -1) {
List<Integer> addedClassIndices = new ArrayList<>(classesMap.values());
Collections.sort(addedClassIndices);
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);
}
int mappingStart = oldClassIndex;
for (int i = oldClassIndex; i < totalClassCount; i++) {
if (!classesRemap.containsKey(i)) {
for (int j = mappingStart; j < totalClassCount; j++) {
if (!classesRemap.containsValue(j)) {
classesRemap.put(i, j);
mappingStart = j + 1;
break;
}
}
}
}
pack.abc.reorganizeClasses(classesRemap);
}
((Tag) pack.abc.parentTag).setModified(true);
deinitReplacement(pack);//successfull finish
}
}
@Override
public boolean isAvailable() {
String flexLocation = Configuration.flexSdkLocation.get();
return !(flexLocation.isEmpty() || (!new File(MxmlcRunner.getMxmlcPath(flexLocation)).exists()));
}
@Override
public synchronized void initReplacement(ScriptPack pack) {
if (tempDir != null) {
deinitReplacement(pack);
}
try {
tempDir = Files.createTempDirectory("ffdec-mxmlc-replace").toFile();
File pkgDir = tempDir;
pkgDir = tempDir;
for (String pkgPart : pack.getClassPath().packageStr.toList()) {
if (!pkgPart.isEmpty()) {
pkgDir = new File(pkgDir, pkgPart);
}
}
pkgDir.mkdirs();
File scriptFileToCompile = new File(pkgDir, pack.getClassPath().className + ".as");
File compiledSwfFile = new File(pkgDir, "out.swf");
File swcFile = new File(pkgDir, "out.swc");
swcFile = new File(pkgDir, "out.swc");
//Make copy without the old script
SWF swfCopy = recompileSWF(pack.getSwf());
List<ABC> modAbcs = new ArrayList<>();
List<ScriptPack> copyPacks = swfCopy.getAS3Packs();
@@ -135,6 +275,7 @@ public class MxmlcAs3ScriptReplacer extends MxmlcRunner implements As3ScriptRepl
removedPacks.add(sp);
}
}
//Export subclasses so they can be compiled by Flex, but ONLY STUBS.
//No method code to avoid code compilation problems.
//This compiled code won't be used at all in original SWF,
@@ -146,115 +287,25 @@ public class MxmlcAs3ScriptReplacer extends MxmlcRunner implements As3ScriptRepl
for (ABC a : modAbcs) {
a.pack();
}
//Generate SWC file from the modified SWF file.
//Flex then uses the code already present in the SWC, no need to decompile it (hurray!)
SwfToSwcExporter swcExport = new SwfToSwcExporter();
swcExport.exportSwf(swfCopy, swcFile, true);
//Write new script
Helper.writeFile(scriptFileToCompile.getAbsolutePath(), text.getBytes("UTF-8"));
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 = pack.scriptIndex;
int oldClassIndex = -1;
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;
if (oldClassIndex == -1 || traitClassIndex < oldClassIndex) {
oldClassIndex = traitClassIndex;
}
}
}
if (pack.isSimple) {
oldScriptInfo.delete(pack.abc, true);
} else {
//NOO
}
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<>();
pack.abc.mergeABC(newLastAbc,
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
classesMap,
new HashMap<>(),
scriptsMap
);
//Reorder newly created scripts to be in place
//where old script was
List<Integer> addedScriptIndices = new ArrayList<>(scriptsMap.values());
Collections.sort(addedScriptIndices);
List<ScriptInfo> addedScripts = new ArrayList<>();
for (int i = addedScriptIndices.size() - 1; i >= 0; i--) {
int newScriptIndex = addedScriptIndices.get(i);
addedScripts.add(0, pack.abc.script_info.remove(newScriptIndex));
}
for (int i = 0; i < addedScripts.size(); i++) {
pack.abc.script_info.add(oldScriptIndex + i, addedScripts.get(i));
}
//IMPORTANT: Map newly created classes to their position as they
//were in original script because FlashPlayer needs
//parent class to be defined earlier
if (oldClassIndex > -1) {
List<Integer> addedClassIndices = new ArrayList<>(classesMap.values());
Collections.sort(addedClassIndices);
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);
}
int mappingStart = oldClassIndex;
for (int i = oldClassIndex; i < totalClassCount; i++) {
if (!classesRemap.containsKey(i)) {
for (int j = mappingStart; j < totalClassCount; j++) {
if (!classesRemap.containsValue(j)) {
classesRemap.put(i, j);
mappingStart = j + 1;
break;
}
}
}
}
pack.abc.reorganizeClasses(classesRemap);
}
((Tag) pack.abc.parentTag).setModified(true);
}
} finally {
if (tempDir != null && tempDir.exists()) {
deleteFolder(tempDir);
}
} catch (IOException iex) {
//ignore
} catch (InterruptedException ex) {
//ignore
}
this.initedPack = pack;
}
@Override
public boolean isAvailable() {
String flexLocation = Configuration.flexSdkLocation.get();
return !(flexLocation.isEmpty() || (!new File(MxmlcRunner.getMxmlcPath(flexLocation)).exists()));
public synchronized void deinitReplacement(ScriptPack pack) {
if (tempDir != null && tempDir.exists()) {
deleteFolder(tempDir);
}
tempDir = null;
}
}