From 8c5345cfd86f8f7546db4d49c3f5b84fa03d1b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sat, 6 Aug 2016 07:19:52 +0200 Subject: [PATCH] AS3 editation with flex - not really working yet: - needs fixing circular references, etc. (eg. editing class which references classes which references this class) - needs better errors displaying with jump to error line, etc. --- .../src/com/jpexs/decompiler/flash/SWF.java | 10 +- .../com/jpexs/decompiler/flash/abc/ABC.java | 136 ++++++--- .../flash/abc/types/traits/Trait.java | 19 +- .../flash/abc/types/traits/TraitClass.java | 40 +-- .../flash/abc/types/traits/TraitFunction.java | 9 +- .../types/traits/TraitMethodGetterSetter.java | 9 +- .../abc/types/traits/TraitSlotConst.java | 10 +- .../flash/abc/types/traits/Traits.java | 5 +- .../flash/configuration/Configuration.java | 5 + .../flash/exporters/script/Dependency.java | 58 ++++ ...sagesParser.java => DependencyParser.java} | 62 +++-- .../exporters/script/DependencyType.java | 5 + .../exporters/script/LinkReportExporter.java | 33 +-- .../flash/exporters/swf/SwfToSwcExporter.java | 261 ++++++++++++++++++ .../flash/exporters/swf/swc_main_abctag.xml | 216 +++++++++++++++ .../flash/flexsdk/As3ScriptReplacer.java | 152 ++++++++++ .../flash/flexsdk/MxmlcException.java | 15 + .../decompiler/flash/flexsdk/MxmlcRunner.java | 54 ++++ .../flash/importers/SwfXmlImporter.java | 14 +- .../flash/flexsdk/As3ScriptReplacerTest.java | 50 ++++ .../console/CommandLineArgumentParser.java | 33 ++- .../jpexs/decompiler/flash/gui/MainPanel.java | 8 + .../decompiler/flash/gui/abc/ABCPanel.java | 11 +- .../locales/AdvancedSettingsDialog.properties | 3 + .../flash/gui/locales/MainFrame.properties | 3 +- 25 files changed, 1067 insertions(+), 154 deletions(-) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/Dependency.java rename libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/{ImportsUsagesParser.java => DependencyParser.java} (64%) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/DependencyType.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfToSwcExporter.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/swc_main_abctag.xml create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/As3ScriptReplacer.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcException.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcRunner.java create mode 100644 libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/flexsdk/As3ScriptReplacerTest.java 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 f17f66ae3..664af0e18 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -2535,10 +2535,14 @@ public final class SWF implements SWFContainerItem, Timelined { System.gc(); } + public void clearAbcListCache() { + abcList = null; + } + public void clearAllCache() { characters = null; characterIdTags = null; - abcList = null; + clearAbcListCache(); timeline = null; clearReadOnlyListCache(); clearImageCache(); @@ -2890,7 +2894,8 @@ 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 { @@ -2899,6 +2904,7 @@ 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 3c3a87945..29f090534 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 @@ -55,7 +55,11 @@ 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.exporters.script.LinkReportExporter; +import com.jpexs.decompiler.flash.flexsdk.As3ScriptReplacer; +import com.jpexs.decompiler.flash.flexsdk.MxmlcException; import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.flash.tags.Tag; @@ -137,6 +141,25 @@ public class ABC { return method_info.size() - 1; } + public void deleteClass(int class_info, boolean d) { + ABC abc = this; + ClassInfo classInfo = abc.class_info.get(class_info); + classInfo.deleted = d; + InstanceInfo instanceInfo = abc.instance_info.get(class_info); + instanceInfo.deleted = d; + + classInfo.static_traits.delete(abc, d); + abc.method_info.get(classInfo.cinit_index).delete(abc, d); + + instanceInfo.instance_traits.delete(abc, d); + abc.method_info.get(instanceInfo.iinit_index).delete(abc, d); + + int protectedNS = instanceInfo.protectedNS; + if (protectedNS != 0) { + abc.constants.getNamespace(protectedNS).deleted = d; + } + } + public TraitMethodGetterSetter addMethod(int classId, String name, boolean isStatic) { Multiname multiname = new Multiname(); multiname.kind = Multiname.QNAME; @@ -743,10 +766,19 @@ public class ABC { aos.writeU30(ci.cinit_index); aos.writeTraits(ci.static_traits); } - aos.writeU30(script_info.size()); + int numScripts = script_info.size(); for (ScriptInfo si : script_info) { - aos.writeU30(si.init_index); - aos.writeTraits(si.traits); + if (si.deleted) { + numScripts--; + } + } + + aos.writeU30(numScripts); + for (ScriptInfo si : script_info) { + if (!si.deleted) { + aos.writeU30(si.init_index); + aos.writeTraits(si.traits); + } } aos.writeU30(bodies.size()); @@ -964,7 +996,9 @@ public class ABC { public List getScriptPacks(String packagePrefix, List allAbcs) { List ret = new ArrayList<>(); for (int i = 0; i < script_info.size(); i++) { - ret.addAll(script_info.get(i).getPacks(this, i, packagePrefix, allAbcs)); + if (!script_info.get(i).deleted) { + ret.addAll(script_info.get(i).getPacks(this, i, packagePrefix, allAbcs)); + } } return ret; } @@ -1328,67 +1362,81 @@ public class ABC { } public boolean replaceScriptPack(ScriptPack pack, String as) throws AVM2ParseException, CompilationException, IOException, InterruptedException { - 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()); + + final boolean USE_FLEX = true; boolean isSimple = pack.isSimple; - ScriptInfo si = script_info.get(oldIndex); - if (isSimple) { - si.delete(this, true); + 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) { - si.traits.traits.get(t).delete(this, true); + 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(this); + ActionScript3Parser.compile(as, this, otherAbcs, isDocumentClass, scriptName, newClassIndex, oldIndex); - 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; + 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 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); } - List 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 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); pack(); // removes old classes/methods ((Tag) parentTag).setModified(true); return !isSimple; } public void pack() { - for (int c = 0; c < instance_info.size(); c++) { - if (instance_info.get(c).deleted) { - removeClass(c); - c--; - } - } for (int m = 0; m < method_info.size(); m++) { if (method_info.get(m).deleted) { removeMethod(m); m--; } } + for (int c = 0; c < instance_info.size(); c++) { + if (instance_info.get(c).deleted) { + removeClass(c); + c--; + } + } getMethodIndexing(); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java index 35b66c090..0c1446dc4 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java @@ -42,7 +42,9 @@ import com.jpexs.decompiler.flash.abc.types.NamespaceSet; import com.jpexs.decompiler.flash.abc.types.ScriptInfo; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.exporters.script.ImportsUsagesParser; +import com.jpexs.decompiler.flash.exporters.script.Dependency; +import com.jpexs.decompiler.flash.exporters.script.DependencyParser; +import com.jpexs.decompiler.flash.exporters.script.DependencyType; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.tags.ABCContainerTag; @@ -174,14 +176,14 @@ public abstract class Trait implements Cloneable, Serializable { return getName(abc).getNamespace(abc.constants).getName(abc.constants); } - public void getImportsUsages(String ignoredCustom, ABC abc, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { + public void getDependencies(String ignoredCustom, ABC abc, List dependencies, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { if (ignoredCustom == null) { Namespace n = getName(abc).getNamespace(abc.constants); if (n.kind == Namespace.KIND_NAMESPACE) { ignoredCustom = n.getName(abc.constants).toRawString(); } } - ImportsUsagesParser.parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, getName(abc), ignorePackage, fullyQualifiedNames); + DependencyParser.parseUsagesFromMultiname(ignoredCustom, abc, dependencies, uses, getName(abc), ignorePackage, fullyQualifiedNames, DependencyType.NAMESPACE); } private static final String[] builtInClasses = {"ArgumentError", "arguments", "Array", "Boolean", "Class", "Date", "DefinitionError", "Error", "EvalError", "Function", "int", "JSON", "Math", "Namespace", "Number", "Object", "QName", "RangeError", "ReferenceError", "RegExp", "SecurityError", "String", "SyntaxError", "TypeError", "uint", "URIError", "VerifyError", "XML", "XMLList"}; @@ -210,14 +212,21 @@ public abstract class Trait implements Cloneable, Serializable { } //imports - List imports = new ArrayList<>(); + List dependencies = new ArrayList<>(); List uses = new ArrayList<>(); String customNs = null; Namespace ns = getName(abc).getNamespace(abc.constants); if (ns.kind == Namespace.KIND_NAMESPACE) { customNs = ns.getName(abc.constants).toRawString(); } - getImportsUsages(customNs, abc, imports, uses, ignorePackage, new ArrayList<>()); + getDependencies(customNs, abc, dependencies, uses, ignorePackage, new ArrayList<>()); + + List imports = new ArrayList<>(); + for (Dependency d : dependencies) { + if (!imports.contains(d.getId())) { + imports.add(d.getId()); + } + } List importnames = new ArrayList<>(); importnames.addAll(namesInThisPackage); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java index d92552773..2eea9627c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java @@ -25,7 +25,9 @@ import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.Multiname; import com.jpexs.decompiler.flash.abc.types.Namespace; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.exporters.script.ImportsUsagesParser; +import com.jpexs.decompiler.flash.exporters.script.Dependency; +import com.jpexs.decompiler.flash.exporters.script.DependencyParser; +import com.jpexs.decompiler.flash.exporters.script.DependencyType; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.graph.DottedChain; @@ -49,22 +51,7 @@ public class TraitClass extends Trait implements TraitWithSlot { @Override public void delete(ABC abc, boolean d) { - ClassInfo classInfo = abc.class_info.get(class_info); - classInfo.deleted = d; - InstanceInfo instanceInfo = abc.instance_info.get(class_info); - instanceInfo.deleted = d; - - classInfo.static_traits.delete(abc, d); - abc.method_info.get(classInfo.cinit_index).delete(abc, d); - - instanceInfo.instance_traits.delete(abc, d); - abc.method_info.get(instanceInfo.iinit_index).delete(abc, d); - - int protectedNS = instanceInfo.protectedNS; - if (protectedNS != 0) { - abc.constants.getNamespace(protectedNS).deleted = d; - } - + abc.deleteClass(class_info, d); abc.constants.getMultiname(name_index).deleted = d; } @@ -79,32 +66,31 @@ public class TraitClass extends Trait implements TraitWithSlot { } @Override - public void getImportsUsages(String customNs, ABC abc, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { - super.getImportsUsages(customNs, abc, imports, uses, ignorePackage == null ? getPackage(abc) : ignorePackage, fullyQualifiedNames); + public void getDependencies(String customNs, ABC abc, List dependencies, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { + super.getDependencies(customNs, abc, dependencies, uses, ignorePackage == null ? getPackage(abc) : ignorePackage, fullyQualifiedNames); ClassInfo classInfo = abc.class_info.get(class_info); InstanceInfo instanceInfo = abc.instance_info.get(class_info); DottedChain packageName = instanceInfo.getName(abc.constants).getNamespace(abc.constants).getName(abc.constants); //assume not null name - ImportsUsagesParser.parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(instanceInfo.name_index), packageName, fullyQualifiedNames); - + //DependencyParser.parseDependenciesFromMultiname(customNs, abc, dependencies, uses, abc.constants.getMultiname(instanceInfo.name_index), packageName, fullyQualifiedNames); if (instanceInfo.super_index > 0) { - ImportsUsagesParser.parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(instanceInfo.super_index), packageName, fullyQualifiedNames); + DependencyParser.parseDependenciesFromMultiname(customNs, abc, dependencies, uses, abc.constants.getMultiname(instanceInfo.super_index), packageName, fullyQualifiedNames, DependencyType.INHERITANCE); } for (int i : instanceInfo.interfaces) { - ImportsUsagesParser.parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(i), packageName, fullyQualifiedNames); + DependencyParser.parseDependenciesFromMultiname(customNs, abc, dependencies, uses, abc.constants.getMultiname(i), packageName, fullyQualifiedNames, DependencyType.INHERITANCE); } //static - classInfo.static_traits.getImportsUsages(customNs, abc, imports, uses, packageName, fullyQualifiedNames); + classInfo.static_traits.getDependencies(customNs, abc, dependencies, uses, packageName, fullyQualifiedNames); //static initializer - ImportsUsagesParser.parseImportsUsagesFromMethodInfo(customNs, abc, classInfo.cinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList<>()); + DependencyParser.parseDependenciesFromMethodInfo(customNs, abc, classInfo.cinit_index, dependencies, uses, packageName, fullyQualifiedNames, new ArrayList<>()); //instance - instanceInfo.instance_traits.getImportsUsages(customNs, abc, imports, uses, packageName, fullyQualifiedNames); + instanceInfo.instance_traits.getDependencies(customNs, abc, dependencies, uses, packageName, fullyQualifiedNames); //instance initializer - ImportsUsagesParser.parseImportsUsagesFromMethodInfo(customNs, abc, instanceInfo.iinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList<>()); + DependencyParser.parseDependenciesFromMethodInfo(customNs, abc, instanceInfo.iinit_index, dependencies, uses, packageName, fullyQualifiedNames, new ArrayList<>()); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java index b18f5f1f1..b02f88e61 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java @@ -20,7 +20,8 @@ import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.types.ConvertData; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.exporters.script.ImportsUsagesParser; +import com.jpexs.decompiler.flash.exporters.script.Dependency; +import com.jpexs.decompiler.flash.exporters.script.DependencyParser; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; @@ -124,14 +125,14 @@ public class TraitFunction extends Trait implements TraitWithSlot { } @Override - public void getImportsUsages(String customNs, ABC abc, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { + public void getDependencies(String customNs, ABC abc, List dependencies, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { if (ignorePackage == null) { ignorePackage = getPackage(abc); } - super.getImportsUsages(customNs, abc, imports, uses, ignorePackage, fullyQualifiedNames); + super.getDependencies(customNs, abc, dependencies, uses, ignorePackage, fullyQualifiedNames); //if (method_info != 0) { - ImportsUsagesParser.parseImportsUsagesFromMethodInfo(customNs, abc, method_info, imports, uses, ignorePackage, fullyQualifiedNames, new ArrayList<>()); + DependencyParser.parseDependenciesFromMethodInfo(customNs, abc, method_info, dependencies, uses, ignorePackage, fullyQualifiedNames, new ArrayList<>()); } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java index 7487d22f2..8b24560a3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java @@ -23,7 +23,8 @@ import com.jpexs.decompiler.flash.abc.types.MethodInfo; import com.jpexs.decompiler.flash.abc.types.Namespace; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.exporters.script.ImportsUsagesParser; +import com.jpexs.decompiler.flash.exporters.script.Dependency; +import com.jpexs.decompiler.flash.exporters.script.DependencyParser; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; @@ -59,11 +60,11 @@ public class TraitMethodGetterSetter extends Trait { } @Override - public void getImportsUsages(String customNs, ABC abc, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { + public void getDependencies(String customNs, ABC abc, List dependencies, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { if (ignorePackage == null) { ignorePackage = getPackage(abc); } - super.getImportsUsages(customNs, abc, imports, uses, ignorePackage, fullyQualifiedNames); + super.getDependencies(customNs, abc, dependencies, uses, ignorePackage, fullyQualifiedNames); if (customNs == null) { Namespace n = getName(abc).getNamespace(abc.constants); @@ -73,7 +74,7 @@ public class TraitMethodGetterSetter extends Trait { } //if (method_info != 0) { - ImportsUsagesParser.parseImportsUsagesFromMethodInfo(customNs, abc, method_info, imports, uses, ignorePackage, fullyQualifiedNames, new ArrayList<>()); + DependencyParser.parseDependenciesFromMethodInfo(customNs, abc, method_info, dependencies, uses, ignorePackage, fullyQualifiedNames, new ArrayList<>()); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java index d46ec1c8b..2ee16f80f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java @@ -26,7 +26,9 @@ import com.jpexs.decompiler.flash.abc.types.Namespace; import com.jpexs.decompiler.flash.abc.types.ValueKind; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.exporters.script.ImportsUsagesParser; +import com.jpexs.decompiler.flash.exporters.script.Dependency; +import com.jpexs.decompiler.flash.exporters.script.DependencyParser; +import com.jpexs.decompiler.flash.exporters.script.DependencyType; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; @@ -197,12 +199,12 @@ public class TraitSlotConst extends Trait implements TraitWithSlot { } @Override - public void getImportsUsages(String customNs, ABC abc, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { + public void getDependencies(String customNs, ABC abc, List dependencies, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { if (ignorePackage == null) { ignorePackage = getPackage(abc); } - super.getImportsUsages(customNs, abc, imports, uses, ignorePackage, fullyQualifiedNames); - ImportsUsagesParser.parseImportsUsagesFromMultiname(customNs, abc, imports, uses, abc.constants.getMultiname(type_index), getPackage(abc), fullyQualifiedNames); + super.getDependencies(customNs, abc, dependencies, uses, ignorePackage, fullyQualifiedNames); + DependencyParser.parseDependenciesFromMultiname(customNs, abc, dependencies, uses, abc.constants.getMultiname(type_index), getPackage(abc), fullyQualifiedNames, DependencyType.SIGNATURE); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java index ad3ed16dc..b863ed879 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.types.ConvertData; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.script.Dependency; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.graph.DottedChain; @@ -244,9 +245,9 @@ public class Traits implements Cloneable, Serializable { } } - public void getImportsUsages(String customNs, ABC abc, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { + public void getDependencies(String customNs, ABC abc, List dependencies, List uses, DottedChain ignorePackage, List fullyQualifiedNames) { for (Trait t : traits) { - t.getImportsUsages(customNs, abc, imports, uses, ignorePackage, fullyQualifiedNames); + t.getDependencies(customNs, abc, dependencies, uses, ignorePackage, fullyQualifiedNames); } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java index 36cd7e4bb..069800f3c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -539,6 +539,11 @@ public class Configuration { @ConfigurationFile(".*\\.swc$") public static final ConfigurationItem playerLibLocation = null; + @ConfigurationDefaultString("") + @ConfigurationCategory("paths") + @ConfigurationDirectory + public static final ConfigurationItem flexSdkLocation = null; + @ConfigurationDefaultDouble(0.7) @ConfigurationName("gui.avm2.splitPane.vars.dividerLocationPercent") @ConfigurationInternal diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/Dependency.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/Dependency.java new file mode 100644 index 000000000..69a082735 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/Dependency.java @@ -0,0 +1,58 @@ +package com.jpexs.decompiler.flash.exporters.script; + +import com.jpexs.decompiler.graph.DottedChain; +import java.util.Objects; + +public class Dependency { + + private DottedChain id; + private DependencyType type; + + public Dependency(DottedChain id, DependencyType type) { + this.id = id; + this.type = type; + } + + public DottedChain getId() { + return id; + } + + public DependencyType getType() { + return type; + } + + @Override + public String toString() { + return id.toString() + " (" + type + ")"; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 79 * hash + Objects.hashCode(this.id); + hash = 79 * hash + Objects.hashCode(this.type); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Dependency other = (Dependency) obj; + if (!Objects.equals(this.id, other.id)) { + return false; + } + if (this.type != other.type) { + return false; + } + return true; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/ImportsUsagesParser.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/DependencyParser.java similarity index 64% rename from libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/ImportsUsagesParser.java rename to libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/DependencyParser.java index 79637ff6d..a779c35d2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/ImportsUsagesParser.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/DependencyParser.java @@ -37,42 +37,44 @@ import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.graph.DottedChain; import java.util.List; -public class ImportsUsagesParser { +public class DependencyParser { - public static void parseImportsUsagesFromNS(String ignoredCustom, ABC abc, List imports, List uses, int namespace_index, DottedChain ignorePackage, String name) { + public static void parseDependenciesFromNS(String ignoredCustom, ABC abc, List dependencies, List uses, int namespace_index, DottedChain ignorePackage, String name, DependencyType dependencyType) { Namespace ns = abc.constants.getNamespace(namespace_index); if (name.isEmpty()) { name = "*"; } DottedChain newimport = ns.getName(abc.constants); - if (parseUsagesFromNS(ignoredCustom, abc, imports, uses, namespace_index, ignorePackage, name)) { + if (parseUsagesFromNS(ignoredCustom, abc, dependencies, uses, namespace_index, ignorePackage, name)) { return; } else if ((ns.kind != Namespace.KIND_PACKAGE) && (ns.kind != Namespace.KIND_PACKAGE_INTERNAL)) { return; } newimport = newimport.add(name); - if (!imports.contains(newimport)) { + Dependency dep = new Dependency(newimport, dependencyType); + + if (!dependencies.contains(dep)) { DottedChain pkg = newimport.getWithoutLast(); //.substring(0, newimport.lastIndexOf('.')); if (pkg.equals(InitVectorAVM2Item.VECTOR_PACKAGE)) { //special case - is imported always return; } if (!pkg.equals(ignorePackage)) { - imports.add(newimport); + dependencies.add(dep); } } //} } - public static void parseImportsUsagesFromMultiname(String ignoredCustom, ABC abc, List imports, List uses, Multiname m, DottedChain ignorePackage, List fullyQualifiedNames) { + public static void parseDependenciesFromMultiname(String ignoredCustom, ABC abc, List dependencies, List uses, Multiname m, DottedChain ignorePackage, List fullyQualifiedNames, DependencyType dependencyType) { if (m != null) { if (m.kind == Multiname.TYPENAME) { if (m.qname_index != 0) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); + parseDependenciesFromMultiname(ignoredCustom, abc, dependencies, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames, dependencyType); } for (Integer i : m.params) { if (i != 0) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); + parseDependenciesFromMultiname(ignoredCustom, abc, dependencies, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames, dependencyType); } } return; @@ -81,46 +83,47 @@ public class ImportsUsagesParser { String name = m.getName(abc.constants, fullyQualifiedNames, true); NamespaceSet nss = m.getNamespaceSet(abc.constants); if (ns != null) { - parseImportsUsagesFromNS(ignoredCustom, abc, imports, uses, m.namespace_index, ignorePackage, name); + parseDependenciesFromNS(ignoredCustom, abc, dependencies, uses, m.namespace_index, ignorePackage, name, dependencyType); } if (nss != null) { for (int n : nss.namespaces) { - parseImportsUsagesFromNS(ignoredCustom, abc, imports, uses, n, ignorePackage, nss.namespaces.length > 1 ? "" : name); + parseDependenciesFromNS(ignoredCustom, abc, dependencies, uses, n, ignorePackage, nss.namespaces.length > 1 ? "" : name, dependencyType); } } } } - public static void parseImportsUsagesFromMethodInfo(String ignoredCustom, ABC abc, int method_index, List imports, List uses, DottedChain ignorePackage, List fullyQualifiedNames, List visitedMethods) { + public static void parseDependenciesFromMethodInfo(String ignoredCustom, ABC abc, int method_index, List dependencies, List uses, DottedChain ignorePackage, List fullyQualifiedNames, List visitedMethods) { if ((method_index < 0) || (method_index >= abc.method_info.size())) { return; } visitedMethods.add(method_index); if (abc.method_info.get(method_index).ret_type != 0) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(abc.method_info.get(method_index).ret_type), ignorePackage, fullyQualifiedNames); + parseDependenciesFromMultiname(ignoredCustom, abc, dependencies, uses, abc.constants.getMultiname(abc.method_info.get(method_index).ret_type), ignorePackage, fullyQualifiedNames, DependencyType.SIGNATURE); } for (int t : abc.method_info.get(method_index).param_types) { if (t != 0) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(t), ignorePackage, fullyQualifiedNames); + parseDependenciesFromMultiname(ignoredCustom, abc, dependencies, uses, abc.constants.getMultiname(t), ignorePackage, fullyQualifiedNames, DependencyType.SIGNATURE); } } MethodBody body = abc.findBody(method_index); if (body != null) { - body.traits.getImportsUsages(ignoredCustom, abc, imports, uses, ignorePackage, fullyQualifiedNames); + body.traits.getDependencies(ignoredCustom, abc, dependencies, uses, ignorePackage, fullyQualifiedNames); for (ABCException ex : body.exceptions) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(ex.type_index), ignorePackage, fullyQualifiedNames); + parseDependenciesFromMultiname(ignoredCustom, abc, dependencies, uses, abc.constants.getMultiname(ex.type_index), ignorePackage, fullyQualifiedNames, DependencyType.EXPRESSION /* or signature?*/); } for (AVM2Instruction ins : body.getCode().code) { if (ins.definition instanceof AlchemyTypeIns) { DottedChain nimport = AlchemyTypeIns.ALCHEMY_PACKAGE.add(ins.definition.instructionName); - if (!imports.contains(nimport)) { - imports.add(nimport); + Dependency depExp = new Dependency(nimport, DependencyType.EXPRESSION); + if (!dependencies.contains(depExp)) { + dependencies.add(depExp); } } if (ins.definition instanceof NewFunctionIns) { if (ins.operands[0] != method_index) { if (!visitedMethods.contains(ins.operands[0])) { - parseImportsUsagesFromMethodInfo(ignoredCustom, abc, ins.operands[0], imports, uses, ignorePackage, fullyQualifiedNames, visitedMethods); + parseDependenciesFromMethodInfo(ignoredCustom, abc, ins.operands[0], dependencies, uses, ignorePackage, fullyQualifiedNames, visitedMethods); } } } @@ -132,7 +135,7 @@ public class ImportsUsagesParser { int m = ins.operands[0]; if (m != 0) { if (m < abc.constants.getMultinameCount()) { - parseImportsUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(m), ignorePackage, fullyQualifiedNames); + parseDependenciesFromMultiname(ignoredCustom, abc, dependencies, uses, abc.constants.getMultiname(m), ignorePackage, fullyQualifiedNames, DependencyType.EXPRESSION); } } } else { @@ -140,7 +143,7 @@ public class ImportsUsagesParser { if (ins.definition.operands[k] == AVM2Code.DAT_MULTINAME_INDEX) { int multinameIndex = ins.operands[k]; if (multinameIndex < abc.constants.getMultinameCount()) { - parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(multinameIndex), ignorePackage, fullyQualifiedNames); + parseUsagesFromMultiname(ignoredCustom, abc, dependencies, uses, abc.constants.getMultiname(multinameIndex), ignorePackage, fullyQualifiedNames, DependencyType.EXPRESSION); } } } @@ -149,15 +152,15 @@ public class ImportsUsagesParser { } } - public static void parseUsagesFromMultiname(String ignoredCustom, ABC abc, List imports, List uses, Multiname m, DottedChain ignorePackage, List fullyQualifiedNames) { + public static void parseUsagesFromMultiname(String ignoredCustom, ABC abc, List dependencies, List uses, Multiname m, DottedChain ignorePackage, List fullyQualifiedNames, DependencyType dependencyType) { if (m != null) { if (m.kind == Multiname.TYPENAME) { if (m.qname_index != 0) { - parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); + parseUsagesFromMultiname(ignoredCustom, abc, dependencies, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames, dependencyType); } for (Integer i : m.params) { if (i != 0) { - parseUsagesFromMultiname(ignoredCustom, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); + parseUsagesFromMultiname(ignoredCustom, abc, dependencies, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames, dependencyType); } } return; @@ -166,21 +169,21 @@ public class ImportsUsagesParser { String name = m.getName(abc.constants, fullyQualifiedNames, false); NamespaceSet nss = m.getNamespaceSet(abc.constants); if (ns != null) { - parseUsagesFromNS(ignoredCustom, abc, imports, uses, m.namespace_index, ignorePackage, name); + parseUsagesFromNS(ignoredCustom, abc, dependencies, uses, m.namespace_index, ignorePackage, name); } if (nss != null) { if (nss.namespaces.length == 1) { - parseUsagesFromNS(ignoredCustom, abc, imports, uses, nss.namespaces[0], ignorePackage, name); + parseUsagesFromNS(ignoredCustom, abc, dependencies, uses, nss.namespaces[0], ignorePackage, name); } else { for (int n : nss.namespaces) { - parseUsagesFromNS(ignoredCustom, abc, imports, uses, n, ignorePackage, ""); + parseUsagesFromNS(ignoredCustom, abc, dependencies, uses, n, ignorePackage, ""); } } } } } - private static boolean parseUsagesFromNS(String ignoredCustom, ABC abc, List imports, List uses, int namespace_index, DottedChain ignorePackage, String name) { + private static boolean parseUsagesFromNS(String ignoredCustom, ABC abc, List dependencies, List uses, int namespace_index, DottedChain ignorePackage, String name) { Namespace ns = abc.constants.getNamespace(namespace_index); if (ns.kind == Namespace.KIND_NAMESPACE) { @@ -192,8 +195,9 @@ public class ImportsUsagesParser { } if (!nsimport.isEmpty()) { - if (!nsimport.getWithoutLast().equals(ignorePackage) && !imports.contains(nsimport)) { - imports.add(nsimport); + Dependency depNs = new Dependency(nsimport, DependencyType.NAMESPACE); + if (!nsimport.getWithoutLast().equals(ignorePackage) && !dependencies.contains(depNs)) { + dependencies.add(depNs); } if (ignoredCustom != null && nsVal.equals(ignoredCustom)) { return true; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/DependencyType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/DependencyType.java new file mode 100644 index 000000000..a3c3d468e --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/DependencyType.java @@ -0,0 +1,5 @@ +package com.jpexs.decompiler.flash.exporters.script; + +public enum DependencyType { + INHERITANCE, NAMESPACE, SIGNATURE, EXPRESSION +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/LinkReportExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/LinkReportExporter.java index b59db7d8a..222a8fd0d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/LinkReportExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/LinkReportExporter.java @@ -162,22 +162,23 @@ public class LinkReportExporter { for (Trait it : ii.instance_traits.traits) { reportTrait(externalDefs, existingObjects, swf, abc, it); } - List imports = new ArrayList<>(); + List dependencies = new ArrayList<>(); List uses = new ArrayList<>(); sb.append(indent(3)).append("").append(newLineChar); //Automatic - getImportsUsagesFromClass(tc.class_info, null, abc, imports, uses, new DottedChain("FAKE!PACKAGE") /*do not skip any package*/, new ArrayList<>()); - for (DottedChain dc : imports) { + tc.getDependencies(null, abc, dependencies, uses, new DottedChain("FAKE!PACKAGE"), new ArrayList<>()); + for (Dependency dependency : dependencies) { + DottedChain dc = dependency.getId(); if (!"*".equals(dc.getLast())) { //some toplevel "imports" can be only method calls if (dc.getWithoutLast().isEmpty() && !existingObjects.contains(dc)) { continue; } - String dep = dottedChainToId(dc); - if (!allDeps.contains(dep)) { - sb.append(indent(3)).append("").append(newLineChar); - allDeps.add(dep); + String reportDepId = dottedChainToId(dc); + if (!allDeps.contains(reportDepId)) { + sb.append(indent(3)).append("").append(newLineChar); + allDeps.add(reportDepId); } } } @@ -185,22 +186,4 @@ public class LinkReportExporter { return sb.toString(); } - private void getImportsUsagesFromClass(int class_info, String customNs, ABC abc, List imports, List uses, DottedChain ignoredPackage, List fullyQualifiedNames) { - ClassInfo classInfo = abc.class_info.get(class_info); - InstanceInfo instanceInfo = abc.instance_info.get(class_info); - - //static initializer - ImportsUsagesParser.parseImportsUsagesFromMethodInfo(customNs, abc, classInfo.cinit_index, imports, uses, ignoredPackage, fullyQualifiedNames, new ArrayList<>()); - - //instance initializer - ImportsUsagesParser.parseImportsUsagesFromMethodInfo(customNs, abc, instanceInfo.iinit_index, imports, uses, ignoredPackage, fullyQualifiedNames, new ArrayList<>()); - - //static - classInfo.static_traits.getImportsUsages(customNs, abc, imports, uses, ignoredPackage, fullyQualifiedNames); - - //instance - instanceInfo.instance_traits.getImportsUsages(customNs, abc, imports, uses, ignoredPackage, fullyQualifiedNames); - - } - } 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 new file mode 100644 index 000000000..039748896 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfToSwcExporter.java @@ -0,0 +1,261 @@ +package com.jpexs.decompiler.flash.exporters.swf; + +import com.jpexs.decompiler.flash.ReadOnlyTagList; +import com.jpexs.decompiler.flash.SWC; +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.exporters.script.Dependency; +import com.jpexs.decompiler.flash.exporters.script.DependencyType; +import com.jpexs.decompiler.flash.importers.SwfXmlImporter; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.DoABC2Tag; +import com.jpexs.decompiler.flash.tags.SymbolClassTag; +import com.jpexs.decompiler.graph.DottedChain; +import com.jpexs.helpers.Helper; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +public class SwfToSwcExporter { + + private static final String DEPENDENCY_NAMESPACE = "n"; + private static final String DEPENDENCY_INHERITANCE = "i"; + private static final String DEPENDENCY_EXPRESSION = "e"; + private static final String DEPENDENCY_SIGNATURE = "s"; + + private static String sha256(InputStream is) { + String output; + int read; + byte[] buffer = new byte[8192]; + + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + while ((read = is.read(buffer)) > 0) { + digest.update(buffer, 0, read); + } + byte[] hash = digest.digest(); + BigInteger bigInt = new BigInteger(1, hash); + output = bigInt.toString(16); + while (output.length() < 32) { + output = "0" + output; + } + } catch (Exception e) { + return null; + } + + return output; + } + + public SwfToSwcExporter() { + + } + + private String dottedChainToId(DottedChain dc) { + if (dc.getWithoutLast().isEmpty()) { + return dc.getLast(); + } + return dc.getWithoutLast().toRawString() + ":" + dc.getLast(); + } + + private String generateCatalog(SWF swf, byte[] swfBytes, boolean skipDependencies) { + StringBuilder sb = new StringBuilder(); + + final String libraryFileName = "library.swf"; + + final String SWC_VERSION = "1.2"; + final String FLEX_VERSION = "4.6.0"; + final String FLEX_BUILD = "23201"; + final String MINIMUM_SUPPORTED_VERSION = "3.0.0"; + + sb.append("\n") + .append("\n") + .append(" \n") + .append(" \n") + .append(" \n") + .append(" \n") + .append(" \n") + .append(" \n") + .append(" \n") + .append(" \n") + .append(" \n") + .append(" \n"); + + final long TIME_NOW = new Date().getTime(); + + Set definedObjects = new HashSet<>(); + + List abcTagList = swf.getAbcList(); + + List packs = swf.getAS3Packs(); + + for (ScriptPack pack : packs) { + ClassPath cp = pack.getClassPath(); + definedObjects.add(cp.packageStr.add(cp.className)); + } + + List allAbcList = new ArrayList<>(); + for (int i = 0; i < abcTagList.size(); i++) { + allAbcList.add(abcTagList.get(i).getABC()); + } + + for (ABCContainerTag abcContainer : abcTagList) { + if (!(abcContainer instanceof DoABC2Tag)) { + continue; //DoABCTag 1 does not have name + } + DoABC2Tag abcTag = (DoABC2Tag) abcContainer; + String scriptName = abcTag.name; + sb.append(" \n"); + } + String sha256Hash = sha256(new ByteArrayInputStream(swfBytes)); + sb.append(" \n") + .append(" \n") + .append(" \n") + .append(" \n") + .append(" \n") + .append(" \n") + .append(" \n") + .append("\n"); + return sb.toString(); + } + + private static String getDependencyStr(DependencyType depType) { + switch (depType) { + case EXPRESSION: + return DEPENDENCY_EXPRESSION; + case INHERITANCE: + return DEPENDENCY_INHERITANCE; + case SIGNATURE: + return DEPENDENCY_SIGNATURE; + case NAMESPACE: + return DEPENDENCY_NAMESPACE; + default: + return null; + } + } + + private SWF recompileSWF(SWF swf) throws IOException, InterruptedException { + ByteArrayOutputStream swfOrigBaos = new ByteArrayOutputStream(); + swf.saveTo(swfOrigBaos); + return new SWF(new ByteArrayInputStream(swfOrigBaos.toByteArray()), false, false); + } + + public void exportSwf(SWF swf, File outSwcFile, boolean skipDependencies) throws IOException { + + //Make local copy of SWF so we do not modify original + try { + swf = recompileSWF(swf); + 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); + SwfXmlImporter xmlImporter = new SwfXmlImporter(); + DoABC2Tag mainAbcTag = (DoABC2Tag) xmlImporter.importObject(abcTagXml, DoABC2Tag.class, swf); + ReadOnlyTagList list = swf.getTags(); + final String documentClassStub = "_%hash%_flash_display_Sprite"; + String documentClass = documentClassStub.replace("%hash%", HASH); + boolean documentClassSet = false; + for (int i = 0; i < list.size(); i++) { + if (list.get(i) instanceof ABCContainerTag) { + swf.addTag(i, mainAbcTag); + break; + } + } + for (int i = 0; i < list.size(); i++) { + if (list.get(i) instanceof SymbolClassTag) { + SymbolClassTag sct = (SymbolClassTag) list.get(i); + for (int j = 0; j < sct.tags.size(); j++) { + if (sct.tags.get(j) == 0) { + sct.names.set(j, documentClass); + documentClassSet = true; + break; + } + } + } + } + if (!documentClassSet) { + throw new IOException("Original document class not found!"); + } + swf = recompileSWF(swf); + + } catch (Exception ex) { + throw new RuntimeException(ex); + } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + swf.saveTo(baos); + byte[] swfBytes = baos.toByteArray(); + String catalogStr = generateCatalog(swf, swfBytes, skipDependencies); + + File tempFile = new File((outSwcFile.getAbsolutePath()) + ".tmp"); + try (FileOutputStream fos = new FileOutputStream(tempFile); + ZipOutputStream zos = new ZipOutputStream(fos)) { + + ZipEntry libraryEntry = new ZipEntry("library.swf"); + zos.putNextEntry(libraryEntry); + zos.write(swfBytes); + zos.closeEntry(); + + ZipEntry catalogEntry = new ZipEntry("catalog.xml"); + zos.putNextEntry(catalogEntry); + zos.write(catalogStr.getBytes("UTF-8")); + zos.closeEntry(); + + zos.close(); + outSwcFile.delete(); + tempFile.renameTo(outSwcFile); + } finally { + if (tempFile.exists()) { + tempFile.delete(); + } + } + + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/swc_main_abctag.xml b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/swc_main_abctag.xml new file mode 100644 index 000000000..f5dcc83f0 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/swc_main_abctag.xml @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + _%hash%_flash_display_Sprite + + _%hash%_flash_display_Sprite/_%hash%_flash_display_Sprite + Security + flash.system + allowDomain + apply + http://adobe.com/AS3/2006/builtin + _%hash%_flash_display_Sprite/allowDomainInRSL + void + domains + allowInsecureDomain + _%hash%_flash_display_Sprite/allowInsecureDomainInRSL + allowDomainInRSL + __go_to_definition_help + pos + 602 + allowInsecureDomainInRSL + 922 + flash.display + Sprite + Object + EventDispatcher + flash.events + DisplayObject + InteractiveObject + DisplayObjectContainer + ExcludeClass + __go_to_ctor_definition_help + 275 + 130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 16 + + + 17 + + + + + 16 + + + 19 + + + + + + + + + 16 + + + 30 + + + + + 16 + + + 31 + + + + + + + + + + + 0 + + + + + 1 + + + + + + + + + + + + + + + + + + + + 2 + 3 + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/As3ScriptReplacer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/As3ScriptReplacer.java new file mode 100644 index 000000000..bbdedd249 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/As3ScriptReplacer.java @@ -0,0 +1,152 @@ +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.types.InstanceInfo; +import com.jpexs.decompiler.flash.abc.types.ScriptInfo; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.script.LinkReportExporter; +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.DottedChain; +import com.jpexs.helpers.Helper; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; + +public class As3ScriptReplacer extends MxmlcRunner { + + private LinkReportExporter linkReporter; + + public As3ScriptReplacer(String flexSdkPath, LinkReportExporter linkReporter) { + super(flexSdkPath); + this.linkReporter = linkReporter; + } + + private static void deleteFolder(File folder) { + File[] files = folder.listFiles(); + if (files != null) { //some JVMs return null for empty dirs + for (File f : files) { + if (f.isDirectory()) { + deleteFolder(f); + } else { + f.delete(); + } + } + } + folder.delete(); + } + + private SWF recompileSWF(SWF swf) throws IOException, InterruptedException { + ByteArrayOutputStream swfOrigBaos = new ByteArrayOutputStream(); + swf.saveTo(swfOrigBaos); + return new SWF(new ByteArrayInputStream(swfOrigBaos.toByteArray()), false, false); + } + + private boolean isParentDeleted(ABC abc, List allAbcs, DottedChain className) { + int class_index = abc.findClassByName(className); + if (class_index == -1) { + for (ABC a : allAbcs) { + class_index = abc.findClassByName(className); + if (class_index != -1) { + abc = a; + break; + } + } + if (class_index == -1) { + return false; + } + } + InstanceInfo ii = abc.instance_info.get(class_index); + if (ii.deleted) { + return true; + } + if (ii.super_index != 0 && isParentDeleted(abc, allAbcs, abc.constants.getMultiname(ii.super_index).getNameWithNamespace(abc.constants))) { + return true; + } + for (int iface : ii.interfaces) { + if (isParentDeleted(abc, allAbcs, abc.constants.getMultiname(iface).getNameWithNamespace(abc.constants))) { + return true; + } + } + return false; + } + + public void replaceScript(SWF swf, ScriptPack oldPack, String txt) throws IOException, MxmlcException, InterruptedException { + if (!oldPack.isSimple) { + throw new IOException("Cannot compile such file"); //Alchemy, etc. + } + + File tempDir = null; + try { + tempDir = Files.createTempDirectory("ffdec-mxmlc-replace").toFile(); + File pkgDir = tempDir; + for (String pkgPart : oldPack.getClassPath().packageStr.toList()) { + if (!pkgPart.isEmpty()) { + pkgDir = new File(pkgDir, pkgPart); + } + } + pkgDir.mkdirs(); + File scriptFileToCompile = new File(pkgDir, oldPack.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); + List copyPacks = swfCopy.getAS3Packs(); + for (ScriptPack sp : copyPacks) { + if (sp.getClassPath().equals(oldPack.getClassPath())) { + sp.abc.script_info.get(sp.scriptIndex).delete(sp.abc, true); + ((Tag) sp.abc.parentTag).setModified(true); + break; + } + } + //remove all subclasses + for (ScriptPack sp : copyPacks) { + DottedChain dc = sp.getPathPackage().add(sp.getPathScriptName()); + if (isParentDeleted(sp.abc, sp.allABCs, dc)) { + sp.abc.script_info.get(sp.scriptIndex).delete(sp.abc, true); + } + } + SwfToSwcExporter swcExport = new SwfToSwcExporter(); + swcExport.exportSwf(swfCopy, swcFile, true); + + Helper.writeFile(scriptFileToCompile.getAbsolutePath(), txt.getBytes("UTF-8")); + mxmlc("-include-inheritance-dependencies-only", "-warnings=false", "-library-path", swcFile.getAbsolutePath(), "-source-path", tempDir.getAbsolutePath(), "-output", compiledSwfFile.getAbsolutePath(), "-debug=true", scriptFileToCompile.getAbsolutePath()); + + try (FileInputStream fis = new FileInputStream(compiledSwfFile)) { + SWF newSWF = new SWF(fis, false, false); + List newTags = newSWF.getAbcList(); + ScriptInfo oldScriptInfo = oldPack.abc.script_info.get(oldPack.scriptIndex); + if (oldPack.isSimple) { + oldScriptInfo.delete(oldPack.abc, true); + } else { + //NOO + } + int oldTagIndex = swf.getTags().indexOf((Tag) oldPack.abc.parentTag); + if (oldPack.abc.script_info.size() == 1) { + swf.removeTag(oldTagIndex); + } + + for (ABCContainerTag act : newTags) { + ((Tag) act).setSwf(swf); + swf.addTag(oldTagIndex + 1, (Tag) act); + ((Tag) act).setModified(true); + } + ((Tag) oldPack.abc.parentTag).setModified(true); + swf.clearAbcListCache(); + swf.clearScriptCache(); + } + } finally { + if (tempDir != null && tempDir.exists()) { + //deleteFolder(tempDir); + } + } + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcException.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcException.java new file mode 100644 index 000000000..ab8b2187e --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcException.java @@ -0,0 +1,15 @@ +package com.jpexs.decompiler.flash.flexsdk; + +public class MxmlcException extends Exception { + + private String mxmlcErrorOutput; + + public MxmlcException(String mxmlcErrorOutput) { + this.mxmlcErrorOutput = mxmlcErrorOutput; + } + + public String getMxmlcErrorOutput() { + return mxmlcErrorOutput; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcRunner.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcRunner.java new file mode 100644 index 000000000..63a709e74 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcRunner.java @@ -0,0 +1,54 @@ +package com.jpexs.decompiler.flash.flexsdk; + +import com.jpexs.helpers.Helper; +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class MxmlcRunner { + + private String flexSdkPath; + + public MxmlcRunner(String flexSdkPath) { + this.flexSdkPath = flexSdkPath; + } + + public String getMxmlcPath() { + 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(); + System.arraycopy(arguments, 0, runArgs, 1, arguments.length); + System.out.println("" + String.join(" ", runArgs)); + Process proc = null; + try { + proc = Runtime.getRuntime().exec(runArgs); + Helper.readStream(proc.getInputStream()); + String errstring = ""; + try { + errstring = new String(Helper.readStream(proc.getErrorStream()), "UTF-8"); + } catch (UnsupportedEncodingException ex) { + //should not happen + } + proc.waitFor(); + int exitValue = proc.exitValue(); + if (exitValue > 0) { + throw new MxmlcException(errstring); + } + } finally { + if (proc != null) { + try { + proc.destroy(); + } catch (Exception ex2) { + //ignore + } + } + } + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java index 691ef5c7a..59cb5113a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java @@ -151,6 +151,18 @@ public class SwfXmlImporter { } } + public Object importObject(String xml, Class requiredType, SWF swf) throws IOException { + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + try { + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + Document doc = docBuilder.parse(new InputSource(new StringReader(xml))); + return processObject(doc.getDocumentElement(), requiredType, swf, null); + } catch (ParserConfigurationException | SAXException | IllegalArgumentException | IllegalAccessException | NoSuchMethodException | InstantiationException | InvocationTargetException ex) { + Logger.getLogger(SwfXmlImporter.class.getName()).log(Level.SEVERE, null, ex); + } + return null; + } + private Field getField(Class cls, String name) throws NoSuchFieldException { Field field; try { @@ -243,7 +255,7 @@ public class SwfXmlImporter { setFieldValue(field, obj, childObj); } } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException | NoSuchMethodException | InstantiationException | InvocationTargetException ex) { - logger.log(Level.SEVERE, null, ex); + logger.log(Level.SEVERE, "Error while getting val from class " + cls + " field: " + name, ex); } } } 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 new file mode 100644 index 000000000..40dcb7677 --- /dev/null +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/flexsdk/As3ScriptReplacerTest.java @@ -0,0 +1,50 @@ +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 java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.testng.annotations.Test; + +public class As3ScriptReplacerTest { + + @Test + public void testReplace() throws IOException, InterruptedException, Exception { + As3ScriptReplacer replacer = new As3ScriptReplacer(Configuration.flexSdkLocation.get(), new LinkReportExporter()); + SWF swf = new SWF(new BufferedInputStream(new FileInputStream("testdata/as3/as3.swf")), false); + String replacement = "package classes\n" + + "{\n" + + "\n" + + " public dynamic class TestClass1\n" + + " {\n" + + " public var attrib:int = 5;\n" + + " public var sons:Array;\n" + + "\n" + + " public function testHello()\n" + + " {\n" + + " trace(\"helloA\");\n" + + " }\n" + + "\n" + + " public function method(i:int):int\n" + + " {\n" + + " trace(\"methodB\");\n" + + " return 7;\n" + + " }\n" + + " }\n" + + "}"; + List classNames = new ArrayList<>(); + classNames.add("classes.TestClass1"); + List packs = swf.getScriptPacksByClassNames(classNames); + for (ScriptPack sp : packs) { + replacer.replaceScript(swf, sp, replacement); + return; + } + } +} diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index b1abaff39..d0c5261e4 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -88,12 +88,14 @@ import com.jpexs.decompiler.flash.exporters.settings.ShapeExportSettings; import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings; import com.jpexs.decompiler.flash.exporters.settings.SpriteExportSettings; import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings; +import com.jpexs.decompiler.flash.exporters.swf.SwfToSwcExporter; import com.jpexs.decompiler.flash.exporters.swf.SwfXmlExporter; import com.jpexs.decompiler.flash.gui.AppStrings; import com.jpexs.decompiler.flash.gui.Main; import com.jpexs.decompiler.flash.gui.SearchInMemory; import com.jpexs.decompiler.flash.gui.SearchInMemoryListener; import com.jpexs.decompiler.flash.gui.SwfInMemory; +import com.jpexs.decompiler.flash.gui.View; import com.jpexs.decompiler.flash.gui.helpers.CheckResources; import com.jpexs.decompiler.flash.helpers.FileTextWriter; import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; @@ -193,6 +195,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.JOptionPane; /** * @@ -585,6 +588,13 @@ public class CommandLineArgumentParser { out.println(" ...: SWF file to search instance in"); } + if (filter == null || filter.equals("swf2swc")) { + out.println(" " + (cnt++) + ") -swf2swc "); + out.println(" ...generates SWC file from SWF"); + out.println(" ...: Where to save SWC file"); + out.println(" ...: Input SWF file"); + } + printCmdLineUsageExamples(out, filter); } @@ -800,7 +810,9 @@ public class CommandLineArgumentParser { command = nextParam.substring(1); } - if (command.equals("linkreport")) { + if (command.equals("swf2swc")) { + parseSwf2Swc(args); + } else if (command.equals("linkreport")) { parseLinkReport(selectionClasses, args); } else if (command.equals("getinstancemetadata")) { parseGetInstanceMetadata(args); @@ -997,6 +1009,18 @@ public class CommandLineArgumentParser { setConfigurations(args.pop()); } + private static void parseSwf2Swc(Stack args) { + if (args.size() < 2) { + badArguments("swf2swc"); + } + final File outFile = new File(args.pop()); + final File swfFile = new File(args.pop()); + processReadSWF(swfFile, null, (SWF swf, OutputStream stdout) -> { + SwfToSwcExporter exporter = new SwfToSwcExporter(); + exporter.exportSwf(swf, outFile, false); + }); + } + private static void parseLinkReport(List selectionClasses, Stack args) { if (args.isEmpty()) { badArguments("linkreport"); @@ -3348,6 +3372,13 @@ public class CommandLineArgumentParser { } private static void parseImportScript(Stack args) { + + String flexLocation = Configuration.flexSdkLocation.get(); + if (flexLocation.isEmpty() || (!new File(flexLocation).exists())) { + System.err.println("Flex SDK path not set"); + System.exit(1); + } + if (args.size() < 3) { badArguments("importscript"); } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index ce5577f9e..79265b496 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -2146,6 +2146,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } public void importScript(final SWF swf) { + + String flexLocation = Configuration.flexSdkLocation.get(); + if (flexLocation.isEmpty() || (!new File(flexLocation).exists())) { + View.showMessageDialog(null, AppStrings.translate("message.flexpath.notset"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + Main.advancedSettings("paths"); + return; + } + JFileChooser chooser = new JFileChooser(); chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); chooser.setDialogTitle(translate("import.select.directory")); diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java index b4014e147..2c24e0deb 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java @@ -1157,6 +1157,12 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener packs = abc.script_info.get(oldIndex).getPacks(abc, oldIndex, null, pack.allABCs); - if (!packs.isEmpty()) { - - }*/ - String as = decompiledTextArea.getText(); abc.replaceScriptPack(pack, as); lastDecompiled = as; diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties index 97539d86e..08d2a876c 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties @@ -432,3 +432,6 @@ config.description.simplifyExpressions = Evaluate and simplify expressions to ma config.name.resetLetterSpacingOnTextImport = Reset Letter Spacing on text import config.description.resetLetterSpacingOnTextImport = Useful for cyrillic fonts, because they are wider + +config.name.flexSdkLocation = 4) Flex SDK directory path +config.description.flexSdkLocation = Location of Adobe Flex SDK. It is used mostly for AS3 compilation. diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index 677619e94..126e31f6f 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -734,4 +734,5 @@ generic.editor.amf3.help = AMF3 value syntax:\n\ \ * Nonscalar datatypes can be referenced by previously declared "id" attributes with # syntax:\n\ %reference_sample%\n\ \ * Keys in Dictionary entries can be any type\n - \ No newline at end of file + +message.flexpath.notset = Flex SDK not found. Please configure its path in Advanced Settings / Paths (4). \ No newline at end of file