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.
This commit is contained in:
Jindra Petřík
2016-08-06 07:19:52 +02:00
parent c8951d9522
commit 8c5345cfd8
25 changed files with 1067 additions and 154 deletions

View File

@@ -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

View File

@@ -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<ScriptPack> getScriptPacks(String packagePrefix, List<ABC> allAbcs) {
List<ScriptPack> 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<ABC> 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<Integer> todel = new ArrayList<>(new TreeSet<>(pack.traitIndices));
for (int i = todel.size() - 1; i >= 0; i--) {
si.traits.traits.remove((int) todel.get(i));
}
}
script_info.get(oldIndex).setModified(true);
}
List<ABC> otherAbcs = new ArrayList<>(pack.allABCs);
otherAbcs.remove(this);
ActionScript3Parser.compile(as, this, otherAbcs, isDocumentClass, scriptName, newClassIndex, oldIndex);
if (isSimple) {
// Move newly added script to its position
script_info.set(oldIndex, script_info.get(newIndex));
script_info.remove(newIndex);
} else {
script_info.get(newIndex).setModified(true);
//Note: Is deleting traits safe?
List<Integer> todel = new ArrayList<>(new TreeSet<>(pack.traitIndices));
for (int i = todel.size() - 1; i >= 0; i--) {
si.traits.traits.remove((int) todel.get(i));
}
}
script_info.get(oldIndex).setModified(true);
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();
}

View File

@@ -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<DottedChain> imports, List<String> uses, DottedChain ignorePackage, List<DottedChain> fullyQualifiedNames) {
public void getDependencies(String ignoredCustom, ABC abc, List<Dependency> dependencies, List<String> uses, DottedChain ignorePackage, List<DottedChain> 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<DottedChain> imports = new ArrayList<>();
List<Dependency> dependencies = new ArrayList<>();
List<String> 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<DottedChain> imports = new ArrayList<>();
for (Dependency d : dependencies) {
if (!imports.contains(d.getId())) {
imports.add(d.getId());
}
}
List<String> importnames = new ArrayList<>();
importnames.addAll(namesInThisPackage);

View File

@@ -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<DottedChain> imports, List<String> uses, DottedChain ignorePackage, List<DottedChain> fullyQualifiedNames) {
super.getImportsUsages(customNs, abc, imports, uses, ignorePackage == null ? getPackage(abc) : ignorePackage, fullyQualifiedNames);
public void getDependencies(String customNs, ABC abc, List<Dependency> dependencies, List<String> uses, DottedChain ignorePackage, List<DottedChain> 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

View File

@@ -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<DottedChain> imports, List<String> uses, DottedChain ignorePackage, List<DottedChain> fullyQualifiedNames) {
public void getDependencies(String customNs, ABC abc, List<Dependency> dependencies, List<String> uses, DottedChain ignorePackage, List<DottedChain> 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<>());
}
}
}

View File

@@ -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<DottedChain> imports, List<String> uses, DottedChain ignorePackage, List<DottedChain> fullyQualifiedNames) {
public void getDependencies(String customNs, ABC abc, List<Dependency> dependencies, List<String> uses, DottedChain ignorePackage, List<DottedChain> 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<>());
}
}

View File

@@ -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<DottedChain> imports, List<String> uses, DottedChain ignorePackage, List<DottedChain> fullyQualifiedNames) {
public void getDependencies(String customNs, ABC abc, List<Dependency> dependencies, List<String> uses, DottedChain ignorePackage, List<DottedChain> 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

View File

@@ -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<DottedChain> imports, List<String> uses, DottedChain ignorePackage, List<DottedChain> fullyQualifiedNames) {
public void getDependencies(String customNs, ABC abc, List<Dependency> dependencies, List<String> uses, DottedChain ignorePackage, List<DottedChain> fullyQualifiedNames) {
for (Trait t : traits) {
t.getImportsUsages(customNs, abc, imports, uses, ignorePackage, fullyQualifiedNames);
t.getDependencies(customNs, abc, dependencies, uses, ignorePackage, fullyQualifiedNames);
}
}
}

View File

@@ -539,6 +539,11 @@ public class Configuration {
@ConfigurationFile(".*\\.swc$")
public static final ConfigurationItem<String> playerLibLocation = null;
@ConfigurationDefaultString("")
@ConfigurationCategory("paths")
@ConfigurationDirectory
public static final ConfigurationItem<String> flexSdkLocation = null;
@ConfigurationDefaultDouble(0.7)
@ConfigurationName("gui.avm2.splitPane.vars.dividerLocationPercent")
@ConfigurationInternal

View File

@@ -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;
}
}

View File

@@ -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<DottedChain> imports, List<String> uses, int namespace_index, DottedChain ignorePackage, String name) {
public static void parseDependenciesFromNS(String ignoredCustom, ABC abc, List<Dependency> dependencies, List<String> 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<DottedChain> imports, List<String> uses, Multiname m, DottedChain ignorePackage, List<DottedChain> fullyQualifiedNames) {
public static void parseDependenciesFromMultiname(String ignoredCustom, ABC abc, List<Dependency> dependencies, List<String> uses, Multiname m, DottedChain ignorePackage, List<DottedChain> 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<DottedChain> imports, List<String> uses, DottedChain ignorePackage, List<DottedChain> fullyQualifiedNames, List<Integer> visitedMethods) {
public static void parseDependenciesFromMethodInfo(String ignoredCustom, ABC abc, int method_index, List<Dependency> dependencies, List<String> uses, DottedChain ignorePackage, List<DottedChain> fullyQualifiedNames, List<Integer> 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<DottedChain> imports, List<String> uses, Multiname m, DottedChain ignorePackage, List<DottedChain> fullyQualifiedNames) {
public static void parseUsagesFromMultiname(String ignoredCustom, ABC abc, List<Dependency> dependencies, List<String> uses, Multiname m, DottedChain ignorePackage, List<DottedChain> 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<DottedChain> imports, List<String> uses, int namespace_index, DottedChain ignorePackage, String name) {
private static boolean parseUsagesFromNS(String ignoredCustom, ABC abc, List<Dependency> dependencies, List<String> 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;

View File

@@ -0,0 +1,5 @@
package com.jpexs.decompiler.flash.exporters.script;
public enum DependencyType {
INHERITANCE, NAMESPACE, SIGNATURE, EXPRESSION
}

View File

@@ -162,22 +162,23 @@ public class LinkReportExporter {
for (Trait it : ii.instance_traits.traits) {
reportTrait(externalDefs, existingObjects, swf, abc, it);
}
List<DottedChain> imports = new ArrayList<>();
List<Dependency> dependencies = new ArrayList<>();
List<String> uses = new ArrayList<>();
sb.append(indent(3)).append("<dep id=\"AS3\" />").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("<dep id=\"").append(dep).append("\" />").append(newLineChar);
allDeps.add(dep);
String reportDepId = dottedChainToId(dc);
if (!allDeps.contains(reportDepId)) {
sb.append(indent(3)).append("<dep id=\"").append(reportDepId).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<DottedChain> imports, List<String> uses, DottedChain ignoredPackage, List<DottedChain> 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);
}
}

View File

@@ -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("<?xml version=\"1.0\" encoding =\"utf-8\"?>\n")
.append("<swc xmlns=\"http://www.adobe.com/flash/swccatalog/9\">\n")
.append(" <versions>\n")
.append(" <swc version=\"").append(SWC_VERSION).append("\" />\n")
.append(" <flex version=\"").append(FLEX_VERSION).append("\" build=\"").append(FLEX_BUILD).append("\" minimumSupportedVersion=\"").append(MINIMUM_SUPPORTED_VERSION).append("\" />\n")
.append(" </versions>\n")
.append(" <features>\n")
.append(" <feature-script-deps />\n")
.append(" <feature-files />\n")
.append(" </features>\n")
.append(" <libraries>\n")
.append(" <library path=\"").append(libraryFileName).append("\">\n");
final long TIME_NOW = new Date().getTime();
Set<DottedChain> definedObjects = new HashSet<>();
List<ABCContainerTag> abcTagList = swf.getAbcList();
List<ScriptPack> packs = swf.getAS3Packs();
for (ScriptPack pack : packs) {
ClassPath cp = pack.getClassPath();
definedObjects.add(cp.packageStr.add(cp.className));
}
List<ABC> 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(" <script name=\"").append(scriptName).append("\" mod=\"").append(TIME_NOW).append("\">\n");
List<ScriptPack> tagPacks = abcTag.getABC().getScriptPacks(null, allAbcList);
for (ScriptPack pack : tagPacks) {
ClassPath cp = pack.getClassPath();
String defId = dottedChainToId(cp.packageStr.add(cp.className));
sb.append(" <def id=\"").append(defId).append("\" />\n");
List<Dependency> dependencies = new ArrayList<>();
List<String> uses = new ArrayList<>();
pack.abc.script_info.get(pack.scriptIndex).traits.getDependencies(null, pack.abc, dependencies, uses, new DottedChain("NO:PACKAGE"), new ArrayList<>());
Set<DottedChain> allDeps = new HashSet<>();
allDeps.add(new DottedChain("AS3"));
sb.append(" <dep id=\"AS3\" type=\"").append(DEPENDENCY_NAMESPACE).append("\" />\n");
if (!skipDependencies) {
for (Dependency d : dependencies) {
if ("*".equals(d.getId().getLast())) {
continue;
}
//some toplevel "imports" can be only method calls
if (d.getId().getWithoutLast().isEmpty() && !definedObjects.contains(d.getId())) {
continue;
}
if (allDeps.contains(d.getId())) {
continue;
}
sb.append(" <dep id=\"").append(dottedChainToId(d.getId())).append("\" type=\"").append(getDependencyStr(d.getType())).append("\" />\n");
allDeps.add(d.getId());
}
}
}
sb.append(" </script>\n");
}
String sha256Hash = sha256(new ByteArrayInputStream(swfBytes));
sb.append(" <digests>\n")
.append(" <digest type=\"SHA-256\" signed=\"false\" value=\"").append(sha256Hash).append("\" />\n")
.append(" </digests>\n")
.append(" </library>\n")
.append(" </libraries>\n")
.append(" <files>\n")
.append(" </files>\n")
.append("</swc>\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();
}
}
}
}

View File

@@ -0,0 +1,216 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Stub for generating Main tag of SWC file -->
<item flags="1" name="_%hash%_flash_display_Sprite" type="DoABC2Tag">
<abc type="ABC">
<version major="46" minor="16" type="ABCVersion"/>
<constants type="AVM2ConstantPool">
<constant_int/>
<constant_uint/>
<constant_double/>
<constant_decimal/>
<constant_float/>
<constant_float4/>
<constant_string>
<item isNull="true"/>
<item>_%hash%_flash_display_Sprite</item>
<item/>
<item>_%hash%_flash_display_Sprite/_%hash%_flash_display_Sprite</item>
<item>Security</item>
<item>flash.system</item>
<item>allowDomain</item>
<item>apply</item>
<item>http://adobe.com/AS3/2006/builtin</item>
<item>_%hash%_flash_display_Sprite/allowDomainInRSL</item>
<item>void</item>
<item>domains</item>
<item>allowInsecureDomain</item>
<item>_%hash%_flash_display_Sprite/allowInsecureDomainInRSL</item>
<item>allowDomainInRSL</item>
<item>__go_to_definition_help</item>
<item>pos</item>
<item>602</item>
<item>allowInsecureDomainInRSL</item>
<item>922</item>
<item>flash.display</item>
<item>Sprite</item>
<item>Object</item>
<item>EventDispatcher</item>
<item>flash.events</item>
<item>DisplayObject</item>
<item>InteractiveObject</item>
<item>DisplayObjectContainer</item>
<item>ExcludeClass</item>
<item>__go_to_ctor_definition_help</item>
<item>275</item>
<item>130</item>
</constant_string>
<constant_namespace>
<item isNull="true"/>
<item kind="5" name_index="1" type="Namespace"/>
<item kind="22" name_index="5" type="Namespace"/>
<item kind="22" name_index="2" type="Namespace"/>
<item kind="8" name_index="8" type="Namespace"/>
<item kind="22" name_index="20" type="Namespace"/>
<item kind="24" name_index="1" type="Namespace"/>
<item kind="22" name_index="24" type="Namespace"/>
<item kind="22" name_index="2" type="Namespace"/>
</constant_namespace>
<constant_namespace_set/>
<constant_multiname>
<item isNull="true"/>
<item kind="7" name_index="4" namespace_index="2" namespace_set_index="0" qname_index="0" type="Multiname"/>
<item kind="7" name_index="6" namespace_index="3" namespace_set_index="0" qname_index="0" type="Multiname"/>
<item kind="7" name_index="7" namespace_index="4" namespace_set_index="0" qname_index="0" type="Multiname"/>
<item kind="7" name_index="10" namespace_index="3" namespace_set_index="0" qname_index="0" type="Multiname"/>
<item kind="7" name_index="12" namespace_index="3" namespace_set_index="0" qname_index="0" type="Multiname"/>
<item kind="7" name_index="14" namespace_index="3" namespace_set_index="0" qname_index="0" type="Multiname"/>
<item kind="7" name_index="18" namespace_index="3" namespace_set_index="0" qname_index="0" type="Multiname"/>
<item kind="7" name_index="1" namespace_index="3" namespace_set_index="0" qname_index="0" type="Multiname"/>
<item kind="7" name_index="21" namespace_index="5" namespace_set_index="0" qname_index="0" type="Multiname"/>
<item kind="7" name_index="22" namespace_index="3" namespace_set_index="0" qname_index="0" type="Multiname"/>
<item kind="7" name_index="23" namespace_index="7" namespace_set_index="0" qname_index="0" type="Multiname"/>
<item kind="7" name_index="25" namespace_index="5" namespace_set_index="0" qname_index="0" type="Multiname"/>
<item kind="7" name_index="26" namespace_index="5" namespace_set_index="0" qname_index="0" type="Multiname"/>
<item kind="7" name_index="27" namespace_index="5" namespace_set_index="0" qname_index="0" type="Multiname"/>
</constant_multiname>
</constants>
<method_info>
<item flags="0" name_index="2" ret_type="0" type="MethodInfo">
<param_types/>
<optional/>
<paramNames/>
</item>
<item flags="0" name_index="3" ret_type="0" type="MethodInfo">
<param_types/>
<optional/>
<paramNames/>
</item>
<item flags="132" name_index="9" ret_type="4" type="MethodInfo">
<param_types/>
<optional/>
<paramNames/>
</item>
<item flags="132" name_index="13" ret_type="4" type="MethodInfo">
<param_types/>
<optional/>
<paramNames/>
</item>
<item flags="0" name_index="2" ret_type="0" type="MethodInfo">
<param_types/>
<optional/>
<paramNames/>
</item>
</method_info>
<metadata_info>
<item name_index="15" type="MetadataInfo">
<keys>
<item>16</item>
</keys>
<values>
<item>17</item>
</values>
</item>
<item name_index="15" type="MetadataInfo">
<keys>
<item>16</item>
</keys>
<values>
<item>19</item>
</values>
</item>
<item name_index="28" type="MetadataInfo">
<keys/>
<values/>
</item>
<item name_index="29" type="MetadataInfo">
<keys>
<item>16</item>
</keys>
<values>
<item>30</item>
</values>
</item>
<item name_index="15" type="MetadataInfo">
<keys>
<item>16</item>
</keys>
<values>
<item>31</item>
</values>
</item>
</metadata_info>
<instance_info>
<item flags="9" iinit_index="1" name_index="8" protectedNS="6" super_index="9" type="InstanceInfo">
<interfaces/>
<instance_traits type="Traits">
<traits>
<item bytes="064100020100" disp_id="0" fileOffset="1994" kindFlags="4" kindType="1" method_info="2" name_index="6" type="TraitMethodGetterSetter">
<metadata>
<item>0</item>
</metadata>
</item>
<item bytes="074100030101" disp_id="0" fileOffset="2000" kindFlags="4" kindType="1" method_info="3" name_index="7" type="TraitMethodGetterSetter">
<metadata>
<item>1</item>
</metadata>
</item>
</traits>
</instance_traits>
</item>
</instance_info>
<class_info>
<item cinit_index="0" type="ClassInfo">
<static_traits type="Traits">
<traits/>
</static_traits>
</item>
</class_info>
<script_info>
<item init_index="4" type="ScriptInfo">
<traits type="Traits">
<traits>
<item bytes="0844010003020304" class_info="0" fileOffset="2011" kindFlags="4" kindType="4" name_index="8" slot_id="1" type="TraitClass">
<metadata>
<item>2</item>
<item>3</item>
<item>4</item>
</metadata>
</item>
</traits>
</traits>
</item>
</script_info>
<bodies>
<item codeBytes="d03047" init_scope_depth="8" max_regs="1" max_scope_depth="9" max_stack="1" method_info="0" type="MethodBody">
<exceptions/>
<traits type="Traits">
<traits/>
</traits>
</item>
<item codeBytes="d030d0490047" init_scope_depth="9" max_regs="1" max_scope_depth="10" max_stack="1" method_info="1" type="MethodBody">
<exceptions/>
<traits type="Traits">
<traits/>
</traits>
</item>
<item codeBytes="d0305d016601660220d14603022947" init_scope_depth="9" max_regs="2" max_scope_depth="10" max_stack="3" method_info="2" type="MethodBody">
<exceptions/>
<traits type="Traits">
<traits/>
</traits>
</item>
<item codeBytes="d0305d016601660520d14603022947" init_scope_depth="9" max_regs="2" max_scope_depth="10" max_stack="3" method_info="3" type="MethodBody">
<exceptions/>
<traits type="Traits">
<traits/>
</traits>
</item>
<item codeBytes="d03065005d0a660a305d0b660b305d0c660c305d0d660d305d0e660e305d096609305d09660958001d1d1d1d1d1d680847" init_scope_depth="1" max_regs="1" max_scope_depth="8" max_stack="2" method_info="4" type="MethodBody">
<exceptions/>
<traits type="Traits">
<traits/>
</traits>
</item>
</bodies>
</abc>
</item>

View File

@@ -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<ABC> 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<ScriptPack> 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<ABCContainerTag> 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);
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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
}
}
}
}
}

View File

@@ -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);
}
}
}