diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index c0211c047..1c4880084 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -2502,6 +2502,7 @@ public final class SWF implements SWFContainerItem, Timelined { } public void clearImageCache() { + jtt = null; frameCache.clear(); rectCache.clear(); for (Tag tag : getTags()) { @@ -2898,8 +2899,7 @@ public final class SWF implements SWFContainerItem, Timelined { timelined.setModified(true); timelined.resetTimeline(); } else // timeline should be always the swf here - { - if (removeDependencies) { + if (removeDependencies) { removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline()); timelined.setModified(true); } else { @@ -2908,7 +2908,6 @@ public final class SWF implements SWFContainerItem, Timelined { timelined.setModified(true); } } - } } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java index 6a9ffdacf..fef18dfc8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABC.java @@ -56,19 +56,15 @@ import com.jpexs.decompiler.flash.abc.usages.MethodParamsMultinameUsage; import com.jpexs.decompiler.flash.abc.usages.MethodReturnTypeMultinameUsage; import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; import com.jpexs.decompiler.flash.abc.usages.TypeNameMultinameUsage; -import com.jpexs.decompiler.flash.configuration.Configuration; 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.MxmlcAs3ScriptReplacer; -import com.jpexs.decompiler.flash.flexsdk.MxmlcException; import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; +import com.jpexs.decompiler.flash.importers.As3ScriptReplaceException; 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; -import com.jpexs.decompiler.graph.CompilationException; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.utf8.Utf8PrintWriter; import java.io.IOException; @@ -1405,7 +1401,7 @@ public class ABC { method_info.remove(index); } - public boolean replaceScriptPack(As3ScriptReplacerInterface replacer, ScriptPack pack, String as) throws AVM2ParseException, CompilationException, IOException, InterruptedException { + public boolean replaceScriptPack(As3ScriptReplacerInterface replacer, ScriptPack pack, String as) throws As3ScriptReplaceException, IOException, InterruptedException { replacer.replaceScript(pack, as); ((Tag) parentTag).setModified(true); return pack.isSimple; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfToSwcExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfToSwcExporter.java index 69fed58d3..7622c807a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfToSwcExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfToSwcExporter.java @@ -6,6 +6,7 @@ import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.ClassPath; import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.script.Dependency; import com.jpexs.decompiler.flash.exporters.script.DependencyType; import com.jpexs.decompiler.flash.importers.SwfXmlImporter; @@ -127,14 +128,15 @@ public class SwfToSwcExporter { String defId = dottedChainToId(cp.packageStr.add(cp.className)); sb.append(" \n"); - List dependencies = new ArrayList<>(); - List uses = new ArrayList<>(); - pack.abc.script_info.get(pack.scriptIndex).traits.getDependencies(null, pack.abc, dependencies, uses, new DottedChain("NO:PACKAGE"), new ArrayList<>()); Set allDeps = new HashSet<>(); allDeps.add(new DottedChain("AS3")); sb.append(" \n"); if (!skipDependencies) { + List dependencies = new ArrayList<>(); + List uses = new ArrayList<>(); + pack.abc.script_info.get(pack.scriptIndex).traits.getDependencies(null, pack.abc, dependencies, uses, new DottedChain("NO:PACKAGE"), new ArrayList<>()); + for (Dependency d : dependencies) { if ("*".equals(d.getId().getLast())) { continue; @@ -185,14 +187,22 @@ public class SwfToSwcExporter { 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 static void printDelay(String title, long t1, long t2) { + //System.out.println("time " + title + ": " + (t2 - t1)); } public void exportSwf(SWF swf, File outSwcFile, boolean skipDependencies) throws IOException { - + long t4 = 0; //Make local copy of SWF so we do not modify original try { + long t1 = System.currentTimeMillis(); swf = recompileSWF(swf); + long t2 = System.currentTimeMillis(); + printDelay("swc.recompile", t1, t2); + final String HASH = "myhash"; String abcTagXml = new String(Helper.readStream(SwfToSwcExporter.class.getResourceAsStream("/com/jpexs/decompiler/flash/exporters/swf/swc_main_abctag.xml")), "UTF-8"); abcTagXml = abcTagXml.replace("%hash%", HASH); @@ -208,6 +218,7 @@ public class SwfToSwcExporter { break; } } + for (int i = 0; i < list.size(); i++) { if (list.get(i) instanceof SymbolClassTag) { SymbolClassTag sct = (SymbolClassTag) list.get(i); @@ -220,20 +231,33 @@ public class SwfToSwcExporter { } } } + long t3 = System.currentTimeMillis(); + printDelay("swc.documentTag", t2, t3); + if (!documentClassSet) { throw new IOException("Original document class not found!"); } swf = recompileSWF(swf); + t4 = System.currentTimeMillis(); + printDelay("swc.recompile2", t3, t4); } catch (Exception ex) { + ex.printStackTrace(); + System.exit(0); throw new RuntimeException(ex); } ByteArrayOutputStream baos = new ByteArrayOutputStream(); swf.saveTo(baos); byte[] swfBytes = baos.toByteArray(); + long t5 = System.currentTimeMillis(); + printDelay("swc.getBytes", t4, t5); + String catalogStr = generateCatalog(swf, swfBytes, skipDependencies); + long t6 = System.currentTimeMillis(); + printDelay("swc.generateCatalog", t5, t6); + File tempFile = new File((outSwcFile.getAbsolutePath()) + ".tmp"); FileOutputStream fos = null; ZipOutputStream zos = null; @@ -277,6 +301,8 @@ public class SwfToSwcExporter { tempFile.delete(); } } + long t7 = System.currentTimeMillis(); + printDelay("swc.zip", t6, t7); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcAs3ScriptReplacer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcAs3ScriptReplacer.java index 22c09859a..f450c2e16 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcAs3ScriptReplacer.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcAs3ScriptReplacer.java @@ -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 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()) + "(?.*)\\((?[0-9]+)\\): col: (?[0-9]+) (?.*)$"); + String err = ex1.getMxmlcErrorOutput(); + String errLines[] = err.split("\r?\n"); + List 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 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 classesMap = new HashMap<>(); + Map 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 addedScriptIndices = new ArrayList<>(scriptsMap.values()); + Collections.sort(addedScriptIndices); + List 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 addedClassIndices = new ArrayList<>(classesMap.values()); + Collections.sort(addedClassIndices); + int totalClassCount = pack.abc.class_info.size(); + Map 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 modAbcs = new ArrayList<>(); List 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 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 classesMap = new HashMap<>(); - Map 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 addedScriptIndices = new ArrayList<>(scriptsMap.values()); - Collections.sort(addedScriptIndices); - List 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 addedClassIndices = new ArrayList<>(classesMap.values()); - Collections.sort(addedClassIndices); - int totalClassCount = pack.abc.class_info.size(); - Map 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; } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS3ScriptImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS3ScriptImporter.java index e43ed93c2..b423df89a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS3ScriptImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS3ScriptImporter.java @@ -51,10 +51,10 @@ public class AS3ScriptImporter { try { 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) { - logger.log(Level.SEVERE, "%error% on line %line%, file: %file%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)).replace("%file%", fileName)); + } catch (As3ScriptReplaceException asre) { + for (As3ScriptReplaceExceptionItem item : asre.getExceptionItems()) { + logger.log(Level.SEVERE, "%error% on line %line%, column %col%, file: %file%".replace("%error%", item.getMessage()).replace("%line%", Long.toString(item.getLine())).replace("%file%", fileName).replace("%col%", "" + item.getCol())); + } } catch (InterruptedException ex) { logger.log(Level.SEVERE, "error during script import, file: %file%".replace("%file%", fileName), ex); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/As3ScriptReplaceException.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/As3ScriptReplaceException.java new file mode 100644 index 000000000..a283ce9ee --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/As3ScriptReplaceException.java @@ -0,0 +1,27 @@ +package com.jpexs.decompiler.flash.importers; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class As3ScriptReplaceException extends Exception { + + private List exceptionItems; + + public As3ScriptReplaceException(List exceptionItems) { + this.exceptionItems = exceptionItems; + } + + public As3ScriptReplaceException(As3ScriptReplaceExceptionItem exceptionItem) { + this.exceptionItems = new ArrayList<>(); + this.exceptionItems.add(exceptionItem); + } + + public List getExceptionItems() { + return exceptionItems; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/As3ScriptReplaceExceptionItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/As3ScriptReplaceExceptionItem.java new file mode 100644 index 000000000..771a29a72 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/As3ScriptReplaceExceptionItem.java @@ -0,0 +1,44 @@ +package com.jpexs.decompiler.flash.importers; + +public class As3ScriptReplaceExceptionItem { + + private String file; + private int line; + private int col; + private String message; + + public static final int COL_UNKNOWN = -1; + public static final int LINE_UNKNOWN = -1; + + public As3ScriptReplaceExceptionItem(String file, String message, int line) { + this(file, message, line, COL_UNKNOWN); + } + + public As3ScriptReplaceExceptionItem(String file, String message) { + this(file, message, LINE_UNKNOWN, COL_UNKNOWN); + } + + public As3ScriptReplaceExceptionItem(String file, String message, int line, int col) { + this.file = file; + this.line = line; + this.col = col; + this.message = message; + } + + public String getFile() { + return file; + } + + public int getLine() { + return line; + } + + public String getMessage() { + return message; + } + + public int getCol() { + return col; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/As3ScriptReplacerFactory.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/As3ScriptReplacerFactory.java index d28c38213..ad7d0a718 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/As3ScriptReplacerFactory.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/As3ScriptReplacerFactory.java @@ -1,7 +1,6 @@ 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 { @@ -15,7 +14,7 @@ public class As3ScriptReplacerFactory { } public static As3ScriptReplacerInterface createFlex() { - return new MxmlcAs3ScriptReplacer(Configuration.flexSdkLocation.get(), new LinkReportExporter()); + return new MxmlcAs3ScriptReplacer(Configuration.flexSdkLocation.get()); } public static As3ScriptReplacerInterface createFFDec() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/As3ScriptReplacerInterface.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/As3ScriptReplacerInterface.java index d14863048..de7de53ad 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/As3ScriptReplacerInterface.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/As3ScriptReplacerInterface.java @@ -1,13 +1,15 @@ 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; + public void initReplacement(ScriptPack pack); + + public void replaceScript(ScriptPack pack, String text) throws As3ScriptReplaceException, IOException, InterruptedException; + + public void deinitReplacement(ScriptPack pack); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/FFDecAs3ScriptReplacer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/FFDecAs3ScriptReplacer.java index cf3c2ca9a..723dcdf12 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/FFDecAs3ScriptReplacer.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/FFDecAs3ScriptReplacer.java @@ -14,53 +14,62 @@ 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()); + public void replaceScript(ScriptPack pack, String text) throws As3ScriptReplaceException, IOException, InterruptedException { + try { + 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 { + 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) { - si.traits.traits.get(t).delete(abc, true); - } - } + if (si.traits.traits.get(t) instanceof TraitClass) { + TraitClass tc = (TraitClass) si.traits.traits.get(t); + newClassIndex = tc.class_info + 1; + } - 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 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 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); + } catch (AVM2ParseException ex) { + throw new As3ScriptReplaceException(new As3ScriptReplaceExceptionItem(null, ex.text, (int) ex.line)); + } catch (CompilationException ex) { + throw new As3ScriptReplaceException(new As3ScriptReplaceExceptionItem(null, ex.text, ex.line)); } - List 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 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 @@ -69,4 +78,14 @@ public class FFDecAs3ScriptReplacer implements As3ScriptReplacerInterface { return !(swc == null || !swc.exists()); } + @Override + public void initReplacement(ScriptPack pack) { + //empty + } + + @Override + public void deinitReplacement(ScriptPack pack) { + //empty + } + } diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/flexsdk/As3ScriptReplacerTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/flexsdk/As3ScriptReplacerTest.java index c966864cd..5b0f32869 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/flexsdk/As3ScriptReplacerTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/flexsdk/As3ScriptReplacerTest.java @@ -3,9 +3,10 @@ package com.jpexs.decompiler.flash.flexsdk; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.script.LinkReportExporter; +import com.jpexs.helpers.Helper; import java.io.BufferedInputStream; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -13,9 +14,10 @@ import org.testng.annotations.Test; public class As3ScriptReplacerTest { - @Test + //Commented out yet... there's no Flex SDK on build server + //@Test public void testReplace() throws IOException, InterruptedException, Exception { - MxmlcAs3ScriptReplacer replacer = new MxmlcAs3ScriptReplacer(Configuration.flexSdkLocation.get(), new LinkReportExporter()); + MxmlcAs3ScriptReplacer replacer = new MxmlcAs3ScriptReplacer(Configuration.flexSdkLocation.get()); SWF swf = new SWF(new BufferedInputStream(new FileInputStream("testdata/as3/as3.swf")), false); String replacement = "package classes\n" + "{\n" diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index cc5bbea58..c7a8d6284 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -102,6 +102,8 @@ import com.jpexs.decompiler.flash.helpers.FileTextWriter; import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; import com.jpexs.decompiler.flash.importers.AS2ScriptImporter; import com.jpexs.decompiler.flash.importers.AS3ScriptImporter; +import com.jpexs.decompiler.flash.importers.As3ScriptReplaceException; +import com.jpexs.decompiler.flash.importers.As3ScriptReplaceExceptionItem; import com.jpexs.decompiler.flash.importers.As3ScriptReplacerFactory; import com.jpexs.decompiler.flash.importers.As3ScriptReplacerInterface; import com.jpexs.decompiler.flash.importers.BinaryDataImporter; @@ -3581,11 +3583,10 @@ public class CommandLineArgumentParser { try { pack.abc.replaceScriptPack(scriptReplacer, pack, as); - } catch (AVM2ParseException ex) { - System.err.println("%error% on line %line%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line))); - System.exit(1); - } catch (CompilationException ex) { - System.err.println("%error% on line %line%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line))); + } catch (As3ScriptReplaceException asre) { + for (As3ScriptReplaceExceptionItem item : asre.getExceptionItems()) { + logger.log(Level.SEVERE, "%error% on line %line%, column %col%, file: %file%".replace("%error%", item.getMessage()).replace("%line%", Long.toString(item.getLine())).replace("%file%", item.getFile()).replace("%col%", "" + item.getCol())); + } System.exit(1); } } diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java index 3e9d20a93..411382eb3 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java @@ -27,7 +27,6 @@ import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instructions; -import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; import com.jpexs.decompiler.flash.abc.types.ABCException; import com.jpexs.decompiler.flash.abc.types.MethodBody; @@ -69,11 +68,12 @@ import com.jpexs.decompiler.flash.gui.abc.tablemodels.UIntTableModel; import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; import com.jpexs.decompiler.flash.gui.editor.LinkHandler; import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; -import com.jpexs.decompiler.flash.importers.As3ScriptReplacerFactory; +import com.jpexs.decompiler.flash.importers.As3ScriptReplaceException; +import com.jpexs.decompiler.flash.importers.As3ScriptReplaceExceptionItem; +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.treeitems.TreeItem; -import com.jpexs.decompiler.graph.CompilationException; import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; import de.hameister.treetable.MyTreeTable; @@ -117,6 +117,7 @@ import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JToggleButton; import javax.swing.SwingConstants; +import javax.swing.SwingWorker; import javax.swing.border.BevelBorder; import javax.swing.event.EventListenerList; import javax.swing.event.TableModelListener; @@ -135,6 +136,9 @@ import jsyntaxpane.TokenType; */ public class ABCPanel extends JPanel implements ItemListener, SearchListener, TagEditorPanel { + private As3ScriptReplacerInterface scriptReplacer = null; + private ScriptPack pack = null; + private final MainPanel mainPanel; public final TraitsList navigator; @@ -1151,27 +1155,40 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener