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 95678a188..2924eaacf 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 @@ -59,17 +59,21 @@ import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.types.annotations.Internal; import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.utf8.Utf8PrintWriter; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; +import javafx.collections.transformation.SortedList; public class ABC { @@ -107,7 +111,7 @@ public class ABC { public ABCContainerTag parentTag; /* Map from multiname index of namespace value to namespace name**/ - private Map namespaceMap; + private Map namespaceMap; public ABC(ABCContainerTag tag) { this.parentTag = tag; @@ -323,7 +327,7 @@ public class ABC { int mIndex = body.getCode().code.get(ip).operands[0]; if (mIndex > 0) { Multiname m = constants.getMultiname(mIndex); - if (m.getNameWithNamespace(constants, true).equals("flash.utils.getDefinitionByName")) { + if (m.getNameWithNamespace(constants).equals("flash.utils.getDefinitionByName")) { if (ip > 0) { if (body.getCode().code.get(ip - 1).definition instanceof PushStringIns) { int strIndex = body.getCode().code.get(ip - 1).operands[0]; @@ -812,16 +816,16 @@ public class ABC { } } - private Map getNamespaceMap() { + private Map getNamespaceMap() { if (namespaceMap == null) { - Map map = new HashMap<>(); + Map map = new HashMap<>(); for (ScriptInfo si : script_info) { for (Trait t : si.traits.traits) { if (t instanceof TraitSlotConst) { TraitSlotConst s = ((TraitSlotConst) t); if (s.isNamespace()) { String key = constants.getNamespace(s.value_index).getName(constants, true); // assume not null - String val = constants.getMultiname(s.name_index).getNameWithNamespace(constants, true); + DottedChain val = constants.getMultiname(s.name_index).getNameWithNamespace(constants); map.put(key, val); } } @@ -855,15 +859,15 @@ public class ABC { return bodyIdxFromMethodIdx; } - public String nsValueToName(String value) { + public DottedChain nsValueToName(String value) { if (getNamespaceMap().containsKey(value)) { return getNamespaceMap().get(value); } else { String ns = getDeobfuscation().builtInNs(value); if (ns == null) { - return ""; + return new DottedChain(""); } else { - return ns; + return new DottedChain(ns); } } } @@ -1037,8 +1041,8 @@ public class ABC { public int findClassByName(String name) { for (int c = 0; c < instance_info.size(); c++) { - String s = constants.getMultiname(instance_info.get(c).name_index).getNameWithNamespace(constants, true); - if (name.equals(s)) { + DottedChain s = constants.getMultiname(instance_info.get(c).name_index).getNameWithNamespace(constants); + if (name.equals(s.toString())) { return c; } } @@ -1229,31 +1233,54 @@ public class ABC { method_info.remove(index); } - public void replaceScriptPack(ScriptPack pack, String as) throws AVM2ParseException, CompilationException, IOException, InterruptedException { + 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()); + boolean isSimple = pack.isSimple; + ScriptInfo si = script_info.get(oldIndex); - si.delete(this, true); - int newClassIndex = instance_info.size(); - for (Trait t : si.traits.traits) { - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - newClassIndex = tc.class_info + 1; + if (isSimple) { + si.delete(this, true); + } else { + for (int t : pack.traitIndices) { + si.traits.traits.get(t).delete(this, true); } } + + int newClassIndex = instance_info.size(); + for (int t : pack.traitIndices) { + if (si.traits.traits.get(t) instanceof TraitClass) { + TraitClass tc = (TraitClass) si.traits.traits.get(t); + newClassIndex = tc.class_info + 1; + } + + } List otherAbcs = new ArrayList<>(pack.allABCs); + otherAbcs.remove(this); ActionScriptParser.compile(as, this, otherAbcs, isDocumentClass, scriptName, newClassIndex); - // Move newly added script to its position - script_info.set(oldIndex, script_info.get(newIndex)); - script_info.remove(newIndex); + + 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() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java index 684204afe..1c3a8f470 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java @@ -61,6 +61,7 @@ public class ScriptPack extends AS3ClassTreeItem { public final List traitIndices; private final ClassPath path; + public boolean isSimple = false; @Override public SWF getSwf() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/DeclarationAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/DeclarationAVM2Item.java index 32d64eaad..956ecd8d6 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/DeclarationAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/DeclarationAVM2Item.java @@ -85,7 +85,7 @@ public class DeclarationAVM2Item extends AVM2Item { if (lti.value instanceof ConvertAVM2Item) { coerType = ((ConvertAVM2Item) lti.value).type; } - srcData.declaredType = (coerType instanceof TypeItem) ? ((TypeItem) coerType).fullTypeName : "*"; + srcData.declaredType = (coerType instanceof TypeItem) ? ((TypeItem) coerType).fullTypeName.toPrintableString() : "*"; writer.append("var "); writer.append(localName); writer.append(":"); @@ -97,7 +97,7 @@ public class DeclarationAVM2Item extends AVM2Item { SetSlotAVM2Item ssti = (SetSlotAVM2Item) assignment; srcData.localName = ssti.getNameAsStr(localData); srcData.declaration = true; - srcData.declaredType = (type instanceof TypeItem) ? ((TypeItem) type).fullTypeName : "*"; + srcData.declaredType = (type instanceof TypeItem) ? ((TypeItem) type).fullTypeName.toPrintableString() : "*"; writer.append("var "); ssti.getName(writer, localData); writer.append(":"); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/TryAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/TryAVM2Item.java index 3e223f4a1..ab8a69b17 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/TryAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/clauses/TryAVM2Item.java @@ -96,7 +96,7 @@ public class TryAVM2Item extends AVM2Item implements Block { int eti = catchExceptions.get(e).type_index; - data.declaredType = eti <= 0 ? "*" : localData.constantsAvm2.constant_multiname.get(eti).getNameWithNamespace(localData.constantsAvm2, true); + data.declaredType = eti <= 0 ? "*" : localData.constantsAvm2.constant_multiname.get(eti).getNameWithNamespace(localData.constantsAvm2).toPrintableString(); writer.hilightSpecial(localName, HighlightSpecialType.TRY_NAME, e, data); writer.append(":"); writer.hilightSpecial(catchExceptions.get(e).getTypeName(localData.constantsAvm2, localData.fullyQualifiedNames), HighlightSpecialType.TRY_TYPE, e); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java index 13a7d5188..37c42df8c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java @@ -108,6 +108,7 @@ import com.jpexs.decompiler.flash.abc.types.traits.Traits; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.Loop; @@ -1194,7 +1195,7 @@ public class AVM2SourceGenerator implements SourceGenerator { return abc; } - public void generateClass(List importedClasses, List sinitVariables, boolean staticNeedsActivation, List staticInit, List openedNamespaces, int namespace, int initScope, String pkg, ClassInfo classInfo, InstanceInfo instanceInfo, SourceGeneratorLocalData localData, boolean isInterface, String name, String superName, GraphTargetItem extendsVal, List implementsStr, GraphTargetItem constructor, List traitItems, Reference class_index) throws AVM2ParseException, CompilationException { + public void generateClass(List importedClasses, List sinitVariables, boolean staticNeedsActivation, List staticInit, List openedNamespaces, int namespace, int initScope, String pkg, ClassInfo classInfo, InstanceInfo instanceInfo, SourceGeneratorLocalData localData, boolean isInterface, String name, String superName, GraphTargetItem extendsVal, List implementsStr, GraphTargetItem constructor, List traitItems, Reference class_index) throws AVM2ParseException, CompilationException { localData.currentClass = name; localData.pkg = pkg; List ret = new ArrayList<>(); @@ -1706,7 +1707,7 @@ public class AVM2SourceGenerator implements SourceGenerator { TraitSlotConst tsc = (TraitSlotConst) mbody.traits.traits.get(i); GraphTargetItem type = TypeItem.UNBOUNDED; if (tsc.type_index > 0) { - type = new TypeItem(abc.constants.constant_multiname.get(tsc.type_index).getNameWithNamespace(abc.constants, true)); + type = new TypeItem(abc.constants.constant_multiname.get(tsc.type_index).getNameWithNamespace(abc.constants)); } NameAVM2Item d = new NameAVM2Item(type, 0, tsc.getName(abc).getName(abc.constants, new ArrayList<>(), true), NameAVM2Item.getDefaultValue("" + type), true, new ArrayList()); d.setSlotNumber(tsc.slot_id); @@ -1867,7 +1868,7 @@ public class AVM2SourceGenerator implements SourceGenerator { return null; } - private int genNs(List importedClasses, String pkg, String custom, int namespace, List openedNamespaces, SourceGeneratorLocalData localData, int line) throws CompilationException { + private int genNs(List importedClasses, String pkg, String custom, int namespace, List openedNamespaces, SourceGeneratorLocalData localData, int line) throws CompilationException { if (custom != null) { PropertyAVM2Item prop = new PropertyAVM2Item(null, custom, abc, allABCs, openedNamespaces, new ArrayList()); Reference value = new Reference<>(null); @@ -1878,11 +1879,9 @@ public class AVM2SourceGenerator implements SourceGenerator { } if (!resolved) { - String customPkg = ""; - String fullCustom = ""; - for (String imp : importedClasses) { - if (imp.endsWith("." + custom)) { - customPkg = imp.substring(0, imp.lastIndexOf('.')); + DottedChain fullCustom = null; + for (DottedChain imp : importedClasses) { + if (imp.getLast().equals(custom)) { fullCustom = imp; break; } @@ -1895,7 +1894,7 @@ public class AVM2SourceGenerator implements SourceGenerator { for (ScriptInfo si : a.script_info) { for (Trait t : si.traits.traits) { Multiname m = t.getName(a); - if (fullCustom.equals(m.getNameWithNamespace(a.constants, true))) { + if (fullCustom != null && fullCustom.equals(m.getNameWithNamespace(a.constants))) { if (t instanceof TraitSlotConst) { if (((TraitSlotConst) t).isNamespace()) { Namespace ns = a.constants.getNamespace(((TraitSlotConst) t).value_index); @@ -1914,7 +1913,7 @@ public class AVM2SourceGenerator implements SourceGenerator { return namespace; } - public void generateTraitsPhase2(List importedClasses, String pkg, List items, Trait[] traits, List openedNamespaces, SourceGeneratorLocalData localData) throws CompilationException { + public void generateTraitsPhase2(List importedClasses, String pkg, List items, Trait[] traits, List openedNamespaces, SourceGeneratorLocalData localData) throws CompilationException { for (int k = 0; k < items.size(); k++) { GraphTargetItem item = items.get(k); if (traits[k] == null) { @@ -2145,7 +2144,7 @@ public class AVM2SourceGenerator implements SourceGenerator { ScriptInfo si = new ScriptInfo(); localData.currentScript = si; Trait[] traitArr = generateTraitsPhase1(null, null, true, localData, commands, si.traits, class_index); - generateTraitsPhase2(new ArrayList<>(), null/*FIXME*/, commands, traitArr, new ArrayList(), localData); + generateTraitsPhase2(new ArrayList(), null/*FIXME*/, commands, traitArr, new ArrayList(), localData); MethodInfo mi = new MethodInfo(new int[0], 0, 0, 0, new ValueKind[0], new int[0]); MethodBody mb = new MethodBody(); mb.method_info = abc.addMethodInfo(mi); @@ -2457,12 +2456,9 @@ public class AVM2SourceGenerator implements SourceGenerator { TypeItem type = (TypeItem) typeItem; - String name = type.fullTypeName; - String pkg = ""; - if (name.contains(".")) { - pkg = name.substring(0, name.lastIndexOf('.')); - name = name.substring(name.lastIndexOf('.') + 1); - } + DottedChain dname = type.fullTypeName; + String pkg = dname.getWithoutLast().toString(); + String name = dname.getLast(); for (InstanceInfo ii : abc.instance_info) { Multiname mname = abc.constants.constant_multiname.get(ii.name_index); if (mname != null && name.equals(mname.getName(abc.constants, new ArrayList<>(), true))) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java index f20e46a4c..4924fd921 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java @@ -86,6 +86,7 @@ import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.Loop; import com.jpexs.decompiler.graph.TypeItem; @@ -144,7 +145,7 @@ public class ActionScriptParser { return uniqLast; } - private List commands(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, AVM2ParseException { + private List commands(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, AVM2ParseException { List ret = new ArrayList<>(); if (debugMode) { System.out.println("commands:"); @@ -159,7 +160,7 @@ public class ActionScriptParser { return ret; } - private GraphTargetItem type(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, List variables) throws IOException, AVM2ParseException { + private GraphTargetItem type(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, List variables) throws IOException, AVM2ParseException { ParsedSymbol s = lex(); if (s.type == SymbolType.MULTIPLY) { return new UnboundedTypeItem(); @@ -174,7 +175,7 @@ public class ActionScriptParser { return t; } - private GraphTargetItem memberOrCall(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem newcmds, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { + private GraphTargetItem memberOrCall(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem newcmds, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { if (debugMode) { System.out.println("memberOrCall:"); } @@ -223,7 +224,7 @@ public class ActionScriptParser { return ret; } - private GraphTargetItem applyType(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem obj, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { + private GraphTargetItem applyType(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem obj, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { GraphTargetItem ret = obj; ParsedSymbol s = lex(); if (s.type == SymbolType.TYPENAME) { @@ -250,7 +251,7 @@ public class ActionScriptParser { return ret; } - private GraphTargetItem member(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem obj, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { + private GraphTargetItem member(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem obj, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { if (debugMode) { System.out.println("member:"); } @@ -286,7 +287,7 @@ public class ActionScriptParser { s = lex(); GraphTargetItem ns = null; if (s.type == SymbolType.NAMESPACE_OP) { - ns = new UnresolvedAVM2Item(new ArrayList(), importedClasses, false, null, lexer.yyline(), propName, null, openedNamespaces); + ns = new UnresolvedAVM2Item(new ArrayList(), importedClasses, false, null, lexer.yyline(), new DottedChain(propName), null, openedNamespaces); variables.add((UnresolvedAVM2Item) ns); s = lex(); if (s.type == SymbolType.BRACKET_OPEN) { @@ -317,28 +318,31 @@ public class ActionScriptParser { return ret; } - private GraphTargetItem name(TypeItem thisType, String pkg, Reference needsActivation, boolean typeOnly, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables, List importedClasses) throws IOException, AVM2ParseException { + private GraphTargetItem name(TypeItem thisType, String pkg, Reference needsActivation, boolean typeOnly, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables, List importedClasses) throws IOException, AVM2ParseException { ParsedSymbol s = lex(); - String name = ""; + DottedChain name = new DottedChain(); + String name2 = ""; if (s.type == SymbolType.ATTRIBUTE) { - name += "@"; + name2 += "@"; s = lex(); } expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolType.STRING_OP); - name += s.value.toString(); + name2 += s.value.toString(); s = lex(); boolean attrBracket = false; + name.parts.add(name2); while (s.isType(SymbolType.DOT)) { - name += s.value.toString(); //. or :: + //name += s.value.toString(); //. or :: s = lex(); + name2 = ""; if (s.type == SymbolType.ATTRIBUTE) { - name += "@"; + name2 += "@"; s = lex(); if (s.type == SymbolType.MULTIPLY) { - name += s.value.toString(); + name2 += s.value.toString(); } else if (s.group == SymbolGroup.IDENTIFIER) { - name += s.value.toString(); + name2 += s.value.toString(); } else { if (s.type != SymbolType.BRACKET_OPEN) { throw new AVM2ParseException("Attribute identifier or bracket expected", lexer.yyline()); @@ -348,19 +352,16 @@ public class ActionScriptParser { } } else { expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER, SymbolType.NAMESPACE); - name += s.value.toString(); + name2 += s.value.toString(); } + name.parts.add(name2); s = lex(); } String nsname = null; String nsprop = null; GraphTargetItem nspropItem = null; if (s.type == SymbolType.NAMESPACE_OP) { - if (name.contains(".")) { - nsname = name.substring(name.lastIndexOf('.') + 1); - } else { - nsname = name; - } + nsname = name.getLast(); s = lex(); if (s.group == SymbolGroup.IDENTIFIER) { nsprop = s.value.toString(); @@ -368,16 +369,12 @@ public class ActionScriptParser { nspropItem = expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); expectedType(SymbolType.BRACKET_CLOSE); } - if (name.contains(".")) { - name = name.substring(0, name.lastIndexOf('.')); - } else { - name = null; - } + name = name.getWithoutLast(); s = lex(); } GraphTargetItem ret = null; - if (name != null) { + if (!name.parts.isEmpty()) { UnresolvedAVM2Item unr = new UnresolvedAVM2Item(new ArrayList(), importedClasses, typeOnly, null, lexer.yyline(), name, null, openedNamespaces); //unr.setIndex(index); variables.add(unr); @@ -388,7 +385,7 @@ public class ActionScriptParser { if (attr) { nsname = nsname.substring(1); } - UnresolvedAVM2Item ns = new UnresolvedAVM2Item(new ArrayList(), importedClasses, typeOnly, null, lexer.yyline(), nsname, null, openedNamespaces); + UnresolvedAVM2Item ns = new UnresolvedAVM2Item(new ArrayList(), importedClasses, typeOnly, null, lexer.yyline(), new DottedChain(nsname), null, openedNamespaces); variables.add(ns); ret = new NamespacedAVM2Item(ns, nsprop, nspropItem, ret, attr, openedNamespaces, null); } @@ -443,7 +440,7 @@ public class ActionScriptParser { return ret; } - private List call(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { + private List call(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { List ret = new ArrayList<>(); //expected(SymbolType.PARENT_OPEN); //MUST BE HANDLED BY CALLER ParsedSymbol s = lex(); @@ -458,12 +455,12 @@ public class ActionScriptParser { return ret; } - private MethodAVM2Item method(String pkg, boolean isInterface, String customAccess, Reference needsActivation, List importedClasses, boolean override, boolean isFinal, TypeItem thisType, List openedNamespaces, boolean isStatic, int namespace, String functionName, boolean isMethod, List variables) throws IOException, AVM2ParseException { + private MethodAVM2Item method(String pkg, boolean isInterface, String customAccess, Reference needsActivation, List importedClasses, boolean override, boolean isFinal, TypeItem thisType, List openedNamespaces, boolean isStatic, int namespace, String functionName, boolean isMethod, List variables) throws IOException, AVM2ParseException { FunctionAVM2Item f = function(pkg, isInterface, needsActivation, importedClasses, namespace, thisType, openedNamespaces, functionName, isMethod, variables); return new MethodAVM2Item(f.pkg, f.isInterface, customAccess, f.needsActivation, f.hasRest, f.line, override, isFinal, isStatic, f.namespace, functionName, f.paramTypes, f.paramNames, f.paramValues, f.body, f.subvariables, f.retType); } - private FunctionAVM2Item function(String pkg, boolean isInterface, Reference needsActivation, List importedClasses, int namespace, TypeItem thisType, List openedNamespaces, String functionName, boolean isMethod, List variables) throws IOException, AVM2ParseException { + private FunctionAVM2Item function(String pkg, boolean isInterface, Reference needsActivation, List importedClasses, int namespace, TypeItem thisType, List openedNamespaces, String functionName, boolean isMethod, List variables) throws IOException, AVM2ParseException { openedNamespaces = new ArrayList<>(openedNamespaces); //local copy int line = lexer.yyline(); ParsedSymbol s; @@ -544,7 +541,7 @@ public class ActionScriptParser { return new FunctionAVM2Item(pkg, isInterface, needsActivation2.getVal(), namespace, hasRest, line, functionName, paramTypes, paramNames, paramValues, body, subvariables, retType); } - private GraphTargetItem traits(String scriptName, boolean scriptTraits, List sinitVariables, Reference sinitNeedsActivation, List staticInitializer, List importedClasses, int privateNs, int protectedNs, int publicNs, int packageInternalNs, int protectedStaticNs, List openedNamespaces, String pkg, String classNameStr, boolean isInterface, List traits) throws AVM2ParseException, IOException, CompilationException { + private GraphTargetItem traits(String scriptName, boolean scriptTraits, List sinitVariables, Reference sinitNeedsActivation, List staticInitializer, List importedClasses, int privateNs, int protectedNs, int publicNs, int packageInternalNs, int protectedStaticNs, List openedNamespaces, String pkg, String classNameStr, boolean isInterface, List traits) throws AVM2ParseException, IOException, CompilationException { ParsedSymbol s; GraphTargetItem constr = null; TypeItem thisType = pkg == null && classNameStr == null ? null : new TypeItem(pkg == null || "".equals(pkg) ? classNameStr : pkg + "." + classNameStr); @@ -892,7 +889,7 @@ public class ActionScriptParser { return constr; } - private GraphTargetItem classTraits(String scriptName, int gpublicNs, String pkg, List importedClasses, boolean isDynamic, boolean isFinal, List openedNamespaces, String packageName, int namespace, boolean isInterface, String nameStr, GraphTargetItem extendsStr, List implementsStr, List variables) throws IOException, AVM2ParseException, CompilationException { + private GraphTargetItem classTraits(String scriptName, int gpublicNs, String pkg, List importedClasses, boolean isDynamic, boolean isFinal, List openedNamespaces, String packageName, int namespace, boolean isInterface, String nameStr, GraphTargetItem extendsStr, List implementsStr, List variables) throws IOException, AVM2ParseException, CompilationException { GraphTargetItem ret = null; @@ -1049,7 +1046,7 @@ public class ActionScriptParser { } } - private List xmltag(TypeItem thisType, String pkg, Reference usesVars, List openedTags, Reference closedVarTags, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { + private List xmltag(TypeItem thisType, String pkg, Reference usesVars, List openedTags, Reference closedVarTags, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { ParsedSymbol s = null; List rets = new ArrayList<>(); //GraphTargetItem ret = null; @@ -1178,7 +1175,7 @@ public class ActionScriptParser { return rets; } - private GraphTargetItem xml(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { + private GraphTargetItem xml(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { List openedTags = new ArrayList<>(); int closedVarTags = 0; @@ -1189,7 +1186,7 @@ public class ActionScriptParser { return ret; } - private GraphTargetItem command(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, boolean mustBeCommand, List variables) throws IOException, AVM2ParseException { + private GraphTargetItem command(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, boolean mustBeCommand, List variables) throws IOException, AVM2ParseException { LexBufferer buf = new LexBufferer(); lexer.addListener(buf); GraphTargetItem ret = null; @@ -1672,7 +1669,7 @@ public class ActionScriptParser { } - private GraphTargetItem expression(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, AVM2ParseException { + private GraphTargetItem expression(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, AVM2ParseException { return expression(thisType, pkg, needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, allowRemainder, variables); } @@ -1704,7 +1701,7 @@ public class ActionScriptParser { return ret; } - /*private GraphTargetItem expressionRemainder(TypeItem thisType, String pkg, Reference needsActivation, List openedNamespaces, GraphTargetItem expr, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables, List importedClasses) throws IOException, AVM2ParseException { + /*private GraphTargetItem expressionRemainder(TypeItem thisType, String pkg, Reference needsActivation, List openedNamespaces, GraphTargetItem expr, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables, List importedClasses) throws IOException, AVM2ParseException { GraphTargetItem ret = null; ParsedSymbol s = lex(); @@ -1740,7 +1737,7 @@ public class ActionScriptParser { return false; } - private int brackets(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, List ret, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { + private int brackets(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, List ret, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, AVM2ParseException { ParsedSymbol s = lex(); int arrCnt = 0; if (s.type == SymbolType.BRACKET_OPEN) { @@ -1764,7 +1761,7 @@ public class ActionScriptParser { return arrCnt; } - private GraphTargetItem commaExpression(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forInLevel, List variables) throws IOException, AVM2ParseException { + private GraphTargetItem commaExpression(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forInLevel, List variables) throws IOException, AVM2ParseException { GraphTargetItem cmd = null; List expr = new ArrayList<>(); ParsedSymbol s; @@ -1786,7 +1783,7 @@ public class ActionScriptParser { return new CommaExpressionItem(null, expr); } - private GraphTargetItem expression(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, AVM2ParseException { + private GraphTargetItem expression(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, AVM2ParseException { GraphTargetItem prim = expressionPrimary(thisType, pkg, needsActivation, importedClasses, openedNamespaces, allowEmpty, registerVars, inFunction, inMethod, allowRemainder, variables); if (prim == null) { return null; @@ -1816,7 +1813,7 @@ public class ActionScriptParser { return lookahead; } - private GraphTargetItem expression1(GraphTargetItem lhs, int min_precedence, TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, AVM2ParseException { + private GraphTargetItem expression1(GraphTargetItem lhs, int min_precedence, TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, AVM2ParseException { if (debugMode) { System.out.println("expression1:"); } @@ -2029,7 +2026,7 @@ public class ActionScriptParser { return lhs; } - private GraphTargetItem expressionPrimary(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, AVM2ParseException { + private GraphTargetItem expressionPrimary(TypeItem thisType, String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, AVM2ParseException { if (debugMode) { System.out.println("primary:"); } @@ -2279,39 +2276,34 @@ public class ActionScriptParser { s = lex(); } - List importedClasses = new ArrayList<>(); + List importedClasses = new ArrayList<>(); s = lex(); while (s.type == SymbolType.IMPORT) { - String impPackage = ""; - String impName = null; boolean all = false; s = lex(); expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); - impName = s.value.toString(); + DottedChain imp = new DottedChain(); + imp.parts.add(s.value.toString()); s = lex(); + boolean isStar = false; while (s.type == SymbolType.DOT) { - if (!"".equals(impPackage)) { - impPackage += "."; - } - impPackage += impName; s = lex(); if (s.type == SymbolType.MULTIPLY) { - impName = null; + isStar = true; s = lex(); break; } expected(s, lexer.yyline(), SymbolGroup.IDENTIFIER); - - impName = s.value.toString(); + imp.parts.add(s.value.toString()); s = lex(); } - if (impName == null) { - openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(impPackage, true)), 0, true)); + if (isStar) { + openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(imp.toString(), true)), 0, true)); } else { - importedClasses.add(impPackage + "." + impName); + importedClasses.add(imp); } expected(s, lexer.yyline(), SymbolType.SEMICOLON); @@ -2349,7 +2341,7 @@ public class ActionScriptParser { openedNamespaces.add(publicNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true)); List items = new ArrayList<>(); - traits(fileName, true, new ArrayList(), new Reference<>(false), new ArrayList(), new ArrayList<>(), scriptPrivateNs, 0, publicNs, 0, 0, openedNamespaces, null, null, false, items); + traits(fileName, true, new ArrayList(), new Reference<>(false), new ArrayList(), new ArrayList(), scriptPrivateNs, 0, publicNs, 0, 0, openedNamespaces, null, null, false, items); return items; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ClassAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ClassAVM2Item.java index 719e7102c..cd49a855d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ClassAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ClassAVM2Item.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.abc.avm2.parser.script; import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.graph.Block; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.model.ContinueItem; import com.jpexs.decompiler.graph.model.LocalData; @@ -54,7 +55,7 @@ public class ClassAVM2Item extends AVM2Item implements Block { public List sinitVariables; - public List importedClasses; + public List importedClasses; public String pkg; @@ -67,7 +68,7 @@ public class ClassAVM2Item extends AVM2Item implements Block { return ret; } - public ClassAVM2Item(List importedClasses, String pkg, List openedNamespaces, int protectedNs, boolean isDynamic, boolean isFinal, int namespace, String className, GraphTargetItem extendsOp, List implementsOp, List staticInit, boolean staticInitActivation, List sinitVariables, GraphTargetItem constructor, List traits) { + public ClassAVM2Item(List importedClasses, String pkg, List openedNamespaces, int protectedNs, boolean isDynamic, boolean isFinal, int namespace, String className, GraphTargetItem extendsOp, List implementsOp, List staticInit, boolean staticInitActivation, List sinitVariables, GraphTargetItem constructor, List traits) { super(null, NOPRECEDENCE); this.importedClasses = importedClasses; this.pkg = pkg; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/InterfaceAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/InterfaceAVM2Item.java index a21b5cdcb..70d5625e4 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/InterfaceAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/InterfaceAVM2Item.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.abc.avm2.parser.script; import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.model.LocalData; import com.jpexs.decompiler.graph.model.UnboundedTypeItem; @@ -43,9 +44,9 @@ public class InterfaceAVM2Item extends AVM2Item { public String pkg; - public List importedClasses; + public List importedClasses; - public InterfaceAVM2Item(List importedClasses, String pkg, List openedNamespaces, boolean isFinal, int namespace, String name, List superInterfaces, List traits) { + public InterfaceAVM2Item(List importedClasses, String pkg, List openedNamespaces, boolean isFinal, int namespace, String name, List superInterfaces, List traits) { super(null, NOPRECEDENCE); this.importedClasses = importedClasses; this.pkg = pkg; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PackageAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PackageAVM2Item.java index 5a69d0668..65e16378d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PackageAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PackageAVM2Item.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.abc.avm2.parser.script; import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.model.LocalData; import com.jpexs.decompiler.graph.model.UnboundedTypeItem; @@ -34,11 +35,11 @@ public class PackageAVM2Item extends AVM2Item { public String packageName; - public List importedClasses = new ArrayList<>(); + public List importedClasses = new ArrayList<>(); public int publicNs = 0; - public PackageAVM2Item(int publicNs, List importedClasses, String packageName, List items) { + public PackageAVM2Item(int publicNs, List importedClasses, String packageName, List items) { super(null, NOPRECEDENCE); this.publicNs = publicNs; this.importedClasses = importedClasses; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java index 9bff7d6f7..96c238524 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java @@ -44,6 +44,7 @@ import com.jpexs.decompiler.flash.abc.types.traits.Trait; import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.SourceGenerator; @@ -115,7 +116,7 @@ public class PropertyAVM2Item extends AssignableAVM2Item { } return new ApplyTypeAVM2Item(null, obj, params); } else { - return new TypeItem(m.getNameWithNamespace(constants, true)); + return new TypeItem(m.getNameWithNamespace(constants)); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java index 751600bb6..2eaf6af21 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java @@ -16,6 +16,7 @@ */ package com.jpexs.decompiler.flash.abc.avm2.parser.script; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.flash.SourceGeneratorLocalData; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; @@ -48,7 +49,7 @@ import java.util.List; */ public class UnresolvedAVM2Item extends AssignableAVM2Item { - private String name; + private DottedChain name; private int nsKind = -1; @@ -65,7 +66,7 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item { private final boolean mustBeType; - public List importedClasses; + public List importedClasses; public List scopeStack = new ArrayList<>(); @@ -128,7 +129,7 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item { */ public void appendName(String name) { - this.name += "." + name; + this.name.parts.add(name); } public void setDefinition(boolean definition) { @@ -150,15 +151,15 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item { this.assignedValue = storeValue; } - public String getVariableName() { + public DottedChain getVariableName() { return name; } - public void setVariableName(String name) { + public void setVariableName(DottedChain name) { this.name = name; } - public UnresolvedAVM2Item(List subtypes, List importedClasses, boolean mustBeType, GraphTargetItem type, int line, String name, GraphTargetItem storeValue, List openedNamespaces) { + public UnresolvedAVM2Item(List subtypes, List importedClasses, boolean mustBeType, GraphTargetItem type, int line, DottedChain name, GraphTargetItem storeValue, List openedNamespaces) { super(storeValue); this.name = name; this.assignedValue = storeValue; @@ -264,7 +265,7 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item { if (resolved != null) { return resolved.toString(); } - return name; + return name.toString(); } @Override @@ -290,14 +291,7 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item { } public GraphTargetItem resolve(GraphTargetItem thisType, List paramTypes, List paramNames, ABC abc, List otherAbcs, List callStack, List variables) throws CompilationException { - List parts = new ArrayList<>(); - if (name.contains(".")) { - String[] partsArr = name.split("\\."); - parts.addAll(Arrays.asList(partsArr)); - } else { - parts.add(name); - } - + List parts = name.parts; if (scopeStack.isEmpty()) { //Everything is multiname property in with command //search for variable @@ -326,13 +320,9 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item { } } //Search for types in imported classes - for (String imp : importedClasses) { - String impName = imp; - String impPkg = ""; - if (impName.contains(".")) { - impPkg = impName.substring(0, impName.lastIndexOf('.')); - impName = impName.substring(impName.lastIndexOf('.') + 1); - } + for (DottedChain imp : importedClasses) { + String impName = imp.getLast(); + if (impName.equals(parts.get(0))) { TypeItem ret = new TypeItem(imp); resolved = ret; @@ -354,13 +344,13 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item { allAbcs.add(abc); allAbcs.addAll(otherAbcs); for (int i = 0; i < parts.size(); i++) { - String fname = Helper.joinStrings(parts.subList(0, i + 1), "."); + DottedChain fname = new DottedChain(parts.subList(0, i + 1)); //Helper.joinStrings(parts.subList(0, i + 1), "."); for (ABC a : allAbcs) { for (int c = 0; c < a.instance_info.size(); c++) { if (a.instance_info.get(c).deleted) { continue; } - if (a.instance_info.get(c).name_index > 0 && fname.equals(a.instance_info.get(c).getName(a.constants).getNameWithNamespace(a.constants, true))) { + if (a.instance_info.get(c).name_index > 0 && fname.equals(a.instance_info.get(c).getName(a.constants).getNameWithNamespace(a.constants))) { if (!subtypes.isEmpty() && parts.size() > i + 1) { continue; } @@ -400,7 +390,7 @@ public class UnresolvedAVM2Item extends AssignableAVM2Item { if (!subtypes.isEmpty() && parts.size() > 1) { continue; } - TypeItem ret = new TypeItem(a.instance_info.get(c).getName(a.constants).getNameWithNamespace(a.constants, true)); + TypeItem ret = new TypeItem(a.instance_info.get(c).getName(a.constants).getNameWithNamespace(a.constants)); /*for (String s : subtypes) { UnresolvedAVM2Item su = new UnresolvedAVM2Item(new ArrayList<>(), importedClasses, true, null, line, s, null, openedNamespaces); su.resolve(thisType, paramTypes, paramNames, abc, otherAbcs, callStack, variables); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java index 241567799..21aa19a6b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java @@ -1,140 +1,140 @@ -/* - * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash.abc.types; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; -import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.helpers.Helper; -import java.util.ArrayList; -import java.util.List; - -public class InstanceInfo { - - public int name_index; - - public int super_index; - - public int flags; // 1 = sealed, 0 = dynamic, 2 = final, 4 = interface, 8 = ProtectedNs - - public int protectedNS; //if flags & 8 - - public int[] interfaces; - - public int iinit_index; // MethodInfo - constructor - - public Traits instance_traits; - - public static final int CLASS_SEALED = 1; //not dynamic - - public static final int CLASS_FINAL = 2; - - public static final int CLASS_INTERFACE = 4; - - public static final int CLASS_PROTECTEDNS = 8; - - @Internal - public boolean deleted; - - public InstanceInfo() { - instance_traits = new Traits(); - } - - public InstanceInfo(Traits traits) { - instance_traits = traits; - } - - @Override - public String toString() { - return "name_index=" + name_index + " super_index=" + super_index + " flags=" + flags + " protectedNS=" + protectedNS + " interfaces=" + Helper.intArrToString(interfaces) + " method_index=" + iinit_index + "\r\n" + instance_traits.toString(); - } - - public String toString(ABC abc, List fullyQualifiedNames) { - String supIndexStr = "[nothing]"; - if (super_index > 0) { - supIndexStr = abc.constants.getMultiname(super_index).toString(abc.constants, fullyQualifiedNames); - } - return "name_index=" + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " super_index=" + supIndexStr + " flags=" + flags + " protectedNS=" + protectedNS + " interfaces=" + Helper.intArrToString(interfaces) + " method_index=" + iinit_index + "\r\n" + instance_traits.toString(abc, fullyQualifiedNames); - } - - public GraphTextWriter getClassHeaderStr(GraphTextWriter writer, ABC abc, List fullyQualifiedNames, boolean allowPrivate) { - String modifiers; - Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants); - modifiers = ns.getPrefix(abc); - if (!allowPrivate && modifiers.equals("private")) { - modifiers = ""; - } - if (!modifiers.isEmpty()) { - modifiers += " "; - } - - if (isFinal()) { - modifiers += "final "; - } - if (!isInterface() && isDynamic()) { - modifiers += "dynamic "; - } - String objType = "class "; - if (isInterface()) { - objType = "interface "; - } - - writer.appendNoHilight(modifiers + objType); - writer.hilightSpecial(abc.constants.getMultiname(name_index).getName(abc.constants, new ArrayList<>()/* No full names here*/, false), HighlightSpecialType.CLASS_NAME); - - if (super_index > 0) { - String typeName = abc.constants.getMultiname(super_index).getNameWithNamespace(abc.constants, true); - writer.appendNoHilight(" extends "); - writer.hilightSpecial(abc.constants.getMultiname(super_index).getName(abc.constants, fullyQualifiedNames, false), HighlightSpecialType.TYPE_NAME, typeName); - } - if (interfaces.length > 0) { - if (isInterface()) { - writer.appendNoHilight(" extends "); - } else { - writer.appendNoHilight(" implements "); - } - for (int i = 0; i < interfaces.length; i++) { - if (i > 0) { - writer.append(", "); - } - String typeName = abc.constants.getMultiname(interfaces[i]).getNameWithNamespace(abc.constants, true); - writer.hilightSpecial(abc.constants.getMultiname(interfaces[i]).getName(abc.constants, fullyQualifiedNames, false), HighlightSpecialType.TYPE_NAME, typeName); - } - } - - return writer; - } - - public Multiname getName(AVM2ConstantPool constants) { - return constants.getMultiname(name_index); - } - - public boolean isInterface() { - return ((flags & CLASS_INTERFACE) == CLASS_INTERFACE); - } - - public boolean isDynamic() { - return (flags & CLASS_SEALED) == 0; - } - - public boolean isFinal() { - return (flags & CLASS_FINAL) == CLASS_FINAL; - } -} +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.abc.types; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.helpers.Helper; +import java.util.ArrayList; +import java.util.List; + +public class InstanceInfo { + + public int name_index; + + public int super_index; + + public int flags; // 1 = sealed, 0 = dynamic, 2 = final, 4 = interface, 8 = ProtectedNs + + public int protectedNS; //if flags & 8 + + public int[] interfaces; + + public int iinit_index; // MethodInfo - constructor + + public Traits instance_traits; + + public static final int CLASS_SEALED = 1; //not dynamic + + public static final int CLASS_FINAL = 2; + + public static final int CLASS_INTERFACE = 4; + + public static final int CLASS_PROTECTEDNS = 8; + + @Internal + public boolean deleted; + + public InstanceInfo() { + instance_traits = new Traits(); + } + + public InstanceInfo(Traits traits) { + instance_traits = traits; + } + + @Override + public String toString() { + return "name_index=" + name_index + " super_index=" + super_index + " flags=" + flags + " protectedNS=" + protectedNS + " interfaces=" + Helper.intArrToString(interfaces) + " method_index=" + iinit_index + "\r\n" + instance_traits.toString(); + } + + public String toString(ABC abc, List fullyQualifiedNames) { + String supIndexStr = "[nothing]"; + if (super_index > 0) { + supIndexStr = abc.constants.getMultiname(super_index).toString(abc.constants, fullyQualifiedNames); + } + return "name_index=" + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " super_index=" + supIndexStr + " flags=" + flags + " protectedNS=" + protectedNS + " interfaces=" + Helper.intArrToString(interfaces) + " method_index=" + iinit_index + "\r\n" + instance_traits.toString(abc, fullyQualifiedNames); + } + + public GraphTextWriter getClassHeaderStr(GraphTextWriter writer, ABC abc, List fullyQualifiedNames, boolean allowPrivate) { + String modifiers; + Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants); + modifiers = ns.getPrefix(abc); + if (!allowPrivate && modifiers.equals("private")) { + modifiers = ""; + } + if (!modifiers.isEmpty()) { + modifiers += " "; + } + + if (isFinal()) { + modifiers += "final "; + } + if (!isInterface() && isDynamic()) { + modifiers += "dynamic "; + } + String objType = "class "; + if (isInterface()) { + objType = "interface "; + } + + writer.appendNoHilight(modifiers + objType); + writer.hilightSpecial(abc.constants.getMultiname(name_index).getName(abc.constants, new ArrayList()/* No full names here*/, false), HighlightSpecialType.CLASS_NAME); + + if (super_index > 0) { + String typeName = abc.constants.getMultiname(super_index).getNameWithNamespace(abc.constants).toPrintableString(); + writer.appendNoHilight(" extends "); + writer.hilightSpecial(abc.constants.getMultiname(super_index).getName(abc.constants, fullyQualifiedNames, false), HighlightSpecialType.TYPE_NAME, typeName); + } + if (interfaces.length > 0) { + if (isInterface()) { + writer.appendNoHilight(" extends "); + } else { + writer.appendNoHilight(" implements "); + } + for (int i = 0; i < interfaces.length; i++) { + if (i > 0) { + writer.append(", "); + } + String typeName = abc.constants.getMultiname(interfaces[i]).getNameWithNamespace(abc.constants).toPrintableString(); + writer.hilightSpecial(abc.constants.getMultiname(interfaces[i]).getName(abc.constants, fullyQualifiedNames, false), HighlightSpecialType.TYPE_NAME, typeName); + } + } + + return writer; + } + + public Multiname getName(AVM2ConstantPool constants) { + return constants.getMultiname(name_index); + } + + public boolean isInterface() { + return ((flags & CLASS_INTERFACE) == CLASS_INTERFACE); + } + + public boolean isDynamic() { + return (flags & CLASS_SEALED) == 0; + } + + public boolean isFinal() { + return (flags & CLASS_FINAL) == CLASS_FINAL; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java index 5452250a7..4003aa7a4 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java @@ -310,7 +310,7 @@ public class MethodInfo { } String ptype = "*"; if (param_types[i] > 0) { - ptype = constants.getMultiname(param_types[i]).getNameWithNamespace(constants, false); + ptype = constants.getMultiname(param_types[i]).getNameWithNamespace(constants).toPrintableString(); } HighlightData pdata = new HighlightData(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java index f43b499d1..da15bae78 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/Multiname.java @@ -19,7 +19,9 @@ package com.jpexs.decompiler.flash.abc.types; import com.jpexs.decompiler.flash.IdentifiersDeobfuscation; import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.Helper; +import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -288,23 +290,28 @@ public class Multiname { } else { String name = constants.getString(name_index); if (fullyQualifiedNames != null && fullyQualifiedNames.contains(name)) { - return getNameWithNamespace(constants, raw); + DottedChain dc = getNameWithNamespace(constants); + return raw ? dc.toString() : dc.toPrintableString(); } return (isAttribute() ? "@" : "") + (raw ? name : IdentifiersDeobfuscation.printIdentifier(true, name)); } } - public String getNameWithNamespace(AVM2ConstantPool constants, boolean raw) { + public DottedChain getNameWithNamespace(AVM2ConstantPool constants) { StringBuilder ret = new StringBuilder(); Namespace ns = getNamespace(constants); + List chain = new ArrayList<>(); if (ns != null) { - String nsname = ns.getName(constants, raw); + String nsname = ns.getName(constants, true); if (nsname != null && !nsname.isEmpty()) { - ret.append(nsname).append("."); + String parts[] = nsname.split("\\."); + for (String p : parts) { + chain.add(p); + } } } - ret.append(getName(constants, null, raw)); - return ret.toString(); + chain.add(getName(constants, null, true)); + return new DottedChain(chain); } public Namespace getNamespace(AVM2ConstantPool constants) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java index 6a96ba792..42b6e7b63 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java @@ -88,6 +88,9 @@ public class ScriptInfo { } } } + if (ret.size() == 1) { + ret.get(0).isSimple = true; + } return ret; } 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 ccdf13ee7..d0de03bfb 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 @@ -1,200 +1,200 @@ -/* - * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash.abc.types.traits; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.ClassPath; -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.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.helpers.NulWriter; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.helpers.Helper; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -public abstract class Trait implements Serializable { - - private static final int[] EMPTY_METADATA_ARRAY = new int[0]; - - public int name_index; - - public int kindType; - - public int kindFlags; - - public int[] metadata = EMPTY_METADATA_ARRAY; - - public long fileOffset; - - public byte[] bytes; - - public static final int ATTR_Final = 0x1; - - public static final int ATTR_Override = 0x2; - - public static final int ATTR_Metadata = 0x4; - - public static final int TRAIT_SLOT = 0; - - public static final int TRAIT_METHOD = 1; - - public static final int TRAIT_GETTER = 2; - - public static final int TRAIT_SETTER = 3; - - public static final int TRAIT_CLASS = 4; - - public static final int TRAIT_FUNCTION = 5; - - public static final int TRAIT_CONST = 6; - - public abstract void delete(ABC abc, boolean d); - - public String getModifiers(ABC abc, boolean isStatic) { - String ret = ""; - if ((kindFlags & ATTR_Override) > 0) { - ret += "override"; - } - Multiname m = getName(abc); - if (m != null) { - String nsname = ""; - //if (abc.constants.getNamespace(m.namespace_index).kind == Namespace.KIND_NAMESPACE) { - { - for (ABCContainerTag abcTag : abc.getAbcTags()) { - if (m.namespace_index == -1) { - break; - } - nsname = abcTag.getABC().nsValueToName(abc.constants.getNamespace(m.namespace_index).getName(abc.constants, true)); - if (nsname == null) { - break; - } - if (nsname.contains(".")) { - nsname = nsname.substring(nsname.lastIndexOf('.') + 1); - } - if (!nsname.isEmpty()) { - break; - } - } - } - Namespace ns = m.getNamespace(abc.constants); - - if (nsname.contains(":")) { - nsname = ""; - } - - if ((!nsname.isEmpty()) && (!nsname.equals("-"))) { - } else { - if (ns != null) { - if (ns.kind == Namespace.KIND_NAMESPACE) { - nsname = ns.getName(abc.constants, true); - } - } - } - - if (nsname != null && (!nsname.contains(":")) && (!nsname.isEmpty())) { - ret += " " + nsname; - } - if (ns != null) { - ret += " " + ns.getPrefix(abc); - } - } - if (isStatic) { - if ((this instanceof TraitSlotConst) && ((TraitSlotConst) this).isNamespace()) { - //static is automatic - } else { - ret += " static"; - } - } - if ((kindFlags & ATTR_Final) > 0) { - if (!isStatic) { - ret += " final"; - } - } - return ret.trim(); - } - - @Override - public String toString() { - return "name_index=" + name_index + " kind=" + kindType + " metadata=" + Helper.intArrToString(metadata); - } - - public String toString(ABC abc, List fullyQualifiedNames) { - return abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " kind=" + kindType + " metadata=" + Helper.intArrToString(metadata); - } - - public GraphTextWriter toString(Trait parent, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - writer.appendNoHilight(abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " kind=" + kindType + " metadata=" + Helper.intArrToString(metadata)); - return writer; - } - - public void convert(Trait parent, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - } - - public GraphTextWriter toStringPackaged(Trait parent, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants); - if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { - String nsname = ns.getName(abc.constants, false); - writer.appendNoHilight("package"); - if (!nsname.isEmpty()) { - writer.appendNoHilight(" " + nsname); //assume not null name - } - writer.startBlock(); - toString(parent, path, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - writer.endBlock(); - writer.newLine(); - } - return writer; - } - - public void convertPackaged(Trait parent, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants); - if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { - String nsname = ns.getName(abc.constants, false); - convert(parent, path + nsname, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - } - } - - public GraphTextWriter toStringHeader(Trait parent, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - toString(parent, path, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - return writer; - } - - public void convertHeader(Trait parent, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - convert(parent, path, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - } - - public Multiname getName(ABC abc) { - if (name_index == 0) { - return null; - } else { - return abc.constants.getMultiname(name_index); - } - } - - public abstract int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException; - - public ClassPath getPath(ABC abc) { - Multiname name = getName(abc); - Namespace ns = name.getNamespace(abc.constants); - String packageName = ns.getName(abc.constants, false); - String objectName = name.getName(abc.constants, new ArrayList<>(), false); - return new ClassPath(packageName, objectName); //assume not null name - } -} +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.abc.types.traits; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ClassPath; +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.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.NulWriter; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.helpers.Helper; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public abstract class Trait implements Serializable { + + private static final int[] EMPTY_METADATA_ARRAY = new int[0]; + + public int name_index; + + public int kindType; + + public int kindFlags; + + public int[] metadata = EMPTY_METADATA_ARRAY; + + public long fileOffset; + + public byte[] bytes; + + public static final int ATTR_Final = 0x1; + + public static final int ATTR_Override = 0x2; + + public static final int ATTR_Metadata = 0x4; + + public static final int TRAIT_SLOT = 0; + + public static final int TRAIT_METHOD = 1; + + public static final int TRAIT_GETTER = 2; + + public static final int TRAIT_SETTER = 3; + + public static final int TRAIT_CLASS = 4; + + public static final int TRAIT_FUNCTION = 5; + + public static final int TRAIT_CONST = 6; + + public abstract void delete(ABC abc, boolean d); + + public String getModifiers(ABC abc, boolean isStatic) { + String ret = ""; + if ((kindFlags & ATTR_Override) > 0) { + ret += "override"; + } + Multiname m = getName(abc); + if (m != null) { + String nsname = ""; + //if (abc.constants.getNamespace(m.namespace_index).kind == Namespace.KIND_NAMESPACE) { + { + for (ABCContainerTag abcTag : abc.getAbcTags()) { + if (m.namespace_index == -1) { + break; + } + nsname = abcTag.getABC().nsValueToName(abc.constants.getNamespace(m.namespace_index).getName(abc.constants, true)).toString(); + if (nsname == null) { + break; + } + if (nsname.contains(".")) { + nsname = nsname.substring(nsname.lastIndexOf('.') + 1); + } + if (!nsname.isEmpty()) { + break; + } + } + } + Namespace ns = m.getNamespace(abc.constants); + + if (nsname.contains(":")) { + nsname = ""; + } + + if ((!nsname.isEmpty()) && (!nsname.equals("-"))) { + } else { + if (ns != null) { + if (ns.kind == Namespace.KIND_NAMESPACE) { + nsname = ns.getName(abc.constants, true); + } + } + } + + if (nsname != null && (!nsname.contains(":")) && (!nsname.isEmpty())) { + ret += " " + nsname; + } + if (ns != null) { + ret += " " + ns.getPrefix(abc); + } + } + if (isStatic) { + if ((this instanceof TraitSlotConst) && ((TraitSlotConst) this).isNamespace()) { + //static is automatic + } else { + ret += " static"; + } + } + if ((kindFlags & ATTR_Final) > 0) { + if (!isStatic) { + ret += " final"; + } + } + return ret.trim(); + } + + @Override + public String toString() { + return "name_index=" + name_index + " kind=" + kindType + " metadata=" + Helper.intArrToString(metadata); + } + + public String toString(ABC abc, List fullyQualifiedNames) { + return abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " kind=" + kindType + " metadata=" + Helper.intArrToString(metadata); + } + + public GraphTextWriter toString(Trait parent, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + writer.appendNoHilight(abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " kind=" + kindType + " metadata=" + Helper.intArrToString(metadata)); + return writer; + } + + public void convert(Trait parent, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + } + + public GraphTextWriter toStringPackaged(Trait parent, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants); + if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { + String nsname = ns.getName(abc.constants, false); + writer.appendNoHilight("package"); + if (!nsname.isEmpty()) { + writer.appendNoHilight(" " + nsname); //assume not null name + } + writer.startBlock(); + toString(parent, path, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + writer.endBlock(); + writer.newLine(); + } + return writer; + } + + public void convertPackaged(Trait parent, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants); + if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { + String nsname = ns.getName(abc.constants, false); + convert(parent, path + nsname, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + } + } + + public GraphTextWriter toStringHeader(Trait parent, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + toString(parent, path, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + return writer; + } + + public void convertHeader(Trait parent, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + convert(parent, path, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + } + + public Multiname getName(ABC abc) { + if (name_index == 0) { + return null; + } else { + return abc.constants.getMultiname(name_index); + } + } + + public abstract int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException; + + public ClassPath getPath(ABC abc) { + Multiname name = getName(abc); + Namespace ns = name.getNamespace(abc.constants); + String packageName = ns.getName(abc.constants, false); + String objectName = name.getName(abc.constants, new ArrayList(), false); + return new ClassPath(packageName, objectName); //assume not null name + } +} 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 af40b017d..be97762cf 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 @@ -37,6 +37,7 @@ import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.ScopeStack; import com.jpexs.helpers.Helper; import java.util.ArrayList; @@ -90,57 +91,53 @@ public class TraitClass extends Trait implements TraitWithSlot { return "Class " + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " slot=" + slot_id + " class_info=" + class_info + " metadata=" + Helper.intArrToString(metadata); } - private boolean parseUsagesFromNS(ABC abc, List imports, List uses, int namespace_index, String ignorePackage, String name) { + private boolean parseUsagesFromNS(ABC abc, List imports, List uses, int namespace_index, String ignorePackage, String name) { Namespace ns = abc.constants.getNamespace(namespace_index); if (name.isEmpty()) { name = "*"; } - String newimport = ns.getName(abc.constants, ns.kind == Namespace.KIND_NAMESPACE); + String nsname = ns.getName(abc.constants, ns.kind == Namespace.KIND_NAMESPACE); + DottedChain newimport = nsname == null ? new DottedChain() : new DottedChain(nsname.split("\\.")); /*if ((ns.kind != Namespace.KIND_PACKAGE) && (ns.kind != Namespace.KIND_NAMESPACE) && (ns.kind != Namespace.KIND_STATIC_PROTECTED)) { return false; }*/ /*if (ns.kind == Namespace.KIND_NAMESPACE)*/ { - String oldimport = newimport; - newimport = null; + DottedChain oldimport = newimport; + newimport = new DottedChain(); for (ABCContainerTag abcTag : abc.getAbcTags()) { - String newname = abcTag.getABC().nsValueToName(oldimport); - if (newname.equals("-")) { + DottedChain newname = abcTag.getABC().nsValueToName(oldimport == null ? null : oldimport.toString()); + if (newname.toString().equals("-")) { return true; } - if (!newname.isEmpty()) { + if (!newname.toString().isEmpty()) { newimport = newname; break; } } - if (newimport == null) { + if (newimport.parts.isEmpty()) { newimport = oldimport; - newimport += "." + name; + newimport.parts.add(name); } - if (newimport != null && newimport.isEmpty()) { - newimport = null; + if (!newimport.parts.isEmpty() && newimport.toString().isEmpty()) { + newimport.parts.clear(); } - if (newimport != null) { + if (newimport.parts.isEmpty()) { /* if(ns.kind==Namespace.KIND_PACKAGE){ newimport+=".*"; }*/ if (!imports.contains(newimport)) { - if (newimport.contains(":")) { - return true; - } - String pkg = ""; - if (newimport.contains(".")) { - pkg = newimport.substring(0, newimport.lastIndexOf('.')); - } - String usname = newimport; - if (usname.contains(".")) { - usname = usname.substring(usname.lastIndexOf('.') + 1); - } + //?? + /*if (newimport.contains(":")) { + return true; + }*/ + DottedChain pkg = newimport.getWithoutLast(); + String usname = newimport.getLast(); if (ns.kind == Namespace.KIND_PACKAGE) { if (!pkg.equals(ignorePackage)) { - if (!pkg.equals("__AS3__.vec")) { //Automatic import + if (!pkg.toString().equals("__AS3__.vec")) { //Automatic import imports.add(newimport); } } @@ -162,38 +159,39 @@ public class TraitClass extends Trait implements TraitWithSlot { return false; } - private void parseImportsUsagesFromNS(ABC abc, List imports, List uses, int namespace_index, String ignorePackage, String name) { + private void parseImportsUsagesFromNS(ABC abc, List imports, List uses, int namespace_index, String ignorePackage, String name) { Namespace ns = abc.constants.getNamespace(namespace_index); if (name.isEmpty()) { name = "*"; } - String newimport = ns.getName(abc.constants, false); + String niS = ns.getName(abc.constants, false); + DottedChain newimport = niS == null ? new DottedChain() : new DottedChain(niS.split("\\.")); if (parseUsagesFromNS(abc, imports, uses, namespace_index, ignorePackage, name)) { return; } else if ((ns.kind != Namespace.KIND_PACKAGE) && (ns.kind != Namespace.KIND_PACKAGE_INTERNAL)) { return; } - if (newimport == null) { - newimport = ""; - } - //if (!newimport.equals("")) { - newimport += "." + name; - if (newimport.contains(":")) { - return; + if (newimport.parts.isEmpty()) { + newimport = new DottedChain(""); } + newimport.parts.add(name); + //WUT? + /*if (newimport.contains(":")) { + return; + }*/ if (!imports.contains(newimport)) { - String pkg = newimport.substring(0, newimport.lastIndexOf('.')); - if (pkg.equals("__AS3__.vec")) { //special case - is imported always + DottedChain pkg = newimport.getWithoutLast(); //.substring(0, newimport.lastIndexOf('.')); + if (pkg.toString().equals("__AS3__.vec")) { //special case - is imported always return; } - if (!pkg.equals(ignorePackage)) { + if (!pkg.toString().equals(ignorePackage)) { imports.add(newimport); } } //} } - private void parseUsagesFromMultiname(ABC abc, List imports, List uses, Multiname m, String ignorePackage, List fullyQualifiedNames) { + private void parseUsagesFromMultiname(ABC abc, List imports, List uses, Multiname m, String ignorePackage, List fullyQualifiedNames) { if (m != null) { if (m.kind == Multiname.TYPENAME) { if (m.qname_index != 0) { @@ -224,7 +222,7 @@ public class TraitClass extends Trait implements TraitWithSlot { } } - private void parseImportsUsagesFromMultiname(ABC abc, List imports, List uses, Multiname m, String ignorePackage, List fullyQualifiedNames) { + private void parseImportsUsagesFromMultiname(ABC abc, List imports, List uses, Multiname m, String ignorePackage, List fullyQualifiedNames) { if (m != null) { if (m.kind == Multiname.TYPENAME) { if (m.qname_index != 0) { @@ -251,7 +249,7 @@ public class TraitClass extends Trait implements TraitWithSlot { } } - private void parseImportsUsagesFromMethodInfo(ABC abc, int method_index, List imports, List uses, String ignorePackage, List fullyQualifiedNames, List visitedMethods) { + private void parseImportsUsagesFromMethodInfo(ABC abc, int method_index, List imports, List uses, String ignorePackage, List fullyQualifiedNames, List visitedMethods) { if ((method_index < 0) || (method_index >= abc.method_info.size())) { return; } @@ -272,7 +270,7 @@ public class TraitClass extends Trait implements TraitWithSlot { } for (AVM2Instruction ins : body.getCode().code) { if (ins.definition instanceof AlchemyTypeIns) { - String nimport = AlchemyTypeIns.ALCHEMY_PACKAGE + "." + ins.definition.instructionName; + DottedChain nimport = new DottedChain((AlchemyTypeIns.ALCHEMY_PACKAGE + "." + ins.definition.instructionName).split("\\.")); if (!imports.contains(nimport)) { imports.add(nimport); } @@ -306,13 +304,13 @@ public class TraitClass extends Trait implements TraitWithSlot { } } - private void parseImportsUsagesFromTraits(ABC abc, Traits ts, List imports, List uses, String ignorePackage, List fullyQualifiedNames) { + private void parseImportsUsagesFromTraits(ABC abc, Traits ts, List imports, List uses, String ignorePackage, List fullyQualifiedNames) { for (Trait t : ts.traits) { parseImportsUsagesFromTrait(abc, t, imports, uses, ignorePackage, fullyQualifiedNames); } } - private void parseImportsUsagesFromTrait(ABC abc, Trait t, List imports, List uses, String ignorePackage, List fullyQualifiedNames) { + private void parseImportsUsagesFromTrait(ABC abc, Trait t, List imports, List uses, String ignorePackage, List fullyQualifiedNames) { if (t instanceof TraitMethodGetterSetter) { TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t; parseImportsUsagesFromMultiname(abc, imports, uses, abc.constants.getMultiname(tm.name_index), ignorePackage, fullyQualifiedNames); @@ -328,7 +326,7 @@ public class TraitClass extends Trait implements TraitWithSlot { } } - private List getImportsUsages(ABC abc, List imports, List uses, List fullyQualifiedNames) { + private List getImportsUsages(ABC abc, List imports, List uses, List fullyQualifiedNames) { //constructor String packageName = abc.instance_info.get(class_info).getName(abc.constants).getNamespace(abc.constants).getName(abc.constants, false); //assume not null name @@ -384,7 +382,7 @@ public class TraitClass extends Trait implements TraitWithSlot { } //imports - List imports = new ArrayList<>(); + List imports = new ArrayList<>(); List uses = new ArrayList<>(); getImportsUsages(abc, imports, uses, new ArrayList<>()); @@ -392,20 +390,16 @@ public class TraitClass extends Trait implements TraitWithSlot { List importnames = new ArrayList<>(); importnames.addAll(namesInThisPackage); - for (String ipath : imports) { - String name = ipath; - String pkg = ""; - if (name.contains(".")) { - pkg = name.substring(0, name.lastIndexOf('.')); - name = name.substring(name.lastIndexOf('.') + 1); - } - if (importnames.contains(name) || ((!pkg.isEmpty()) && isBuiltInClass(name))) { + for (DottedChain ipath : imports) { + String name = ipath.getLast(); + DottedChain pkg = ipath.getWithoutLast(); + if (importnames.contains(name) || ((!pkg.toString().isEmpty()) && isBuiltInClass(name))) { fullyQualifiedNames.add(name); } else { importnames.add(name); } } - /*List imports2 = new ArrayList<>(); + /*List imports2 = new ArrayList(); for (String path : imports) { String name = path; String pkg = ""; @@ -421,21 +415,23 @@ public class TraitClass extends Trait implements TraitWithSlot { imports = imports2;*/ for (int i = 0; i < imports.size(); i++) { - String imp = imports.get(i); - String pkg = imp.substring(0, imp.lastIndexOf('.')); - String name = imp.substring(imp.lastIndexOf('.') + 1); + DottedChain imp = imports.get(i); + DottedChain pkg = imp.getWithoutLast(); //imp.substring(0, imp.lastIndexOf('.')); + String name = imp.getLast();//imp.substring(imp.lastIndexOf('.') + 1); if (name.equals("*")) { continue; } - if (imports.contains(pkg + ".*")) { + DottedChain dAll = new DottedChain(pkg.parts); + dAll.parts.add("*"); + if (imports.contains(dAll)) { imports.remove(i); i--; } } boolean hasImport = false; - for (String imp : imports) { - if (!imp.startsWith(".")) { + for (DottedChain imp : imports) { + if (!imp.parts.get(0).isEmpty()) { //No imports from root package writer.appendNoHilight("import " + imp + ";").newLine(); hasImport = true; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/ClassNameMultinameUsage.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/ClassNameMultinameUsage.java index a755f3e8e..56f98f9a9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/ClassNameMultinameUsage.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/ClassNameMultinameUsage.java @@ -30,6 +30,6 @@ public class ClassNameMultinameUsage extends InsideClassMultinameUsage implement @Override public String toString() { - return "class " + abc.constants.getMultiname(abc.instance_info.get(classIndex).name_index).getNameWithNamespace(abc.constants, false); + return "class " + abc.constants.getMultiname(abc.instance_info.get(classIndex).name_index).getNameWithNamespace(abc.constants).toPrintableString(); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/InsideClassMultinameUsage.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/InsideClassMultinameUsage.java index ea1c31796..6244374aa 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/InsideClassMultinameUsage.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/InsideClassMultinameUsage.java @@ -36,7 +36,7 @@ public abstract class InsideClassMultinameUsage extends MultinameUsage { @Override public String toString() { - return "class " + abc.constants.getMultiname(abc.instance_info.get(classIndex).name_index).getNameWithNamespace(abc.constants, false); + return "class " + abc.constants.getMultiname(abc.instance_info.get(classIndex).name_index).getNameWithNamespace(abc.constants).toPrintableString(); } public int getMultinameIndex() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/DottedChain.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/DottedChain.java new file mode 100644 index 000000000..b6066cc43 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/DottedChain.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.graph; + +import com.jpexs.decompiler.flash.IdentifiersDeobfuscation; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * + * @author JPEXS + */ +public class DottedChain { + + public List parts = new ArrayList<>(); + + public DottedChain(List parts) { + this.parts = new ArrayList<>(parts); + } + + public DottedChain(String... parts) { + for (int i = 0; i < parts.length; i++) { + this.parts.add(parts[i]); + } + } + + public String getLast() { + if (parts.isEmpty()) { + return ""; + } else { + return parts.get(parts.size() - 1); + } + } + + public DottedChain getWithoutLast() { + List nparts = new ArrayList<>(parts); + if (!nparts.isEmpty()) { + nparts.remove(nparts.size() - 1); + } + return new DottedChain(nparts); + } + + public String toPrintableString() { + String ret = ""; + for (int i = 0; i < parts.size(); i++) { + if (i > 0) { + ret += "."; + } + ret += IdentifiersDeobfuscation.printIdentifier(true, parts.get(0)); + } + return ret; + } + + @Override + public String toString() { + String ret = ""; + for (int i = 0; i < parts.size(); i++) { + if (i > 0) { + ret += "."; + } + ret += parts.get(i); + } + return ret; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 89 * hash + Objects.hashCode(this.parts); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof String) { + obj = new DottedChain(((String) obj).split("\\.")); + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final DottedChain other = (DottedChain) obj; + if (!Objects.equals(this.parts, other.parts)) { + return false; + } + return true; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/TypeItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/TypeItem.java index ad27e56ea..9dc3bc898 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/TypeItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/TypeItem.java @@ -31,33 +31,37 @@ import java.util.Objects; * @author JPEXS */ public class TypeItem extends GraphTargetItem { - + public static TypeItem BOOLEAN = new TypeItem("Boolean"); - + public static TypeItem STRING = new TypeItem("String"); - + public static TypeItem ARRAY = new TypeItem("Array"); - + public static UnboundedTypeItem UNBOUNDED = new UnboundedTypeItem(); - - public String fullTypeName; - - public TypeItem(String fullTypeName) { + + public DottedChain fullTypeName; + + public TypeItem(String s) { + this(s == null ? new DottedChain() : new DottedChain(s.split("\\."))); + } + + public TypeItem(DottedChain fullTypeName) { this(fullTypeName, new ArrayList()); } - - public TypeItem(String fullTypeName, List subtypes) { + + public TypeItem(DottedChain fullTypeName, List subtypes) { super(null, NOPRECEDENCE); this.fullTypeName = fullTypeName; } - + @Override public int hashCode() { int hash = 7; hash = 83 * hash + Objects.hashCode(this.fullTypeName); return hash; } - + @Override public boolean equals(Object obj) { if (obj == null) { @@ -72,37 +76,33 @@ public class TypeItem extends GraphTargetItem { } return true; } - + @Override public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { if (localData.fullyQualifiedNames.contains(fullTypeName)) { - writer.hilightSpecial(IdentifiersDeobfuscation.printNamespace(localData.constantsAvm2 != null, fullTypeName), HighlightSpecialType.TYPE_NAME, fullTypeName); + writer.hilightSpecial(IdentifiersDeobfuscation.printNamespace(localData.constantsAvm2 != null, fullTypeName.toPrintableString()), HighlightSpecialType.TYPE_NAME, fullTypeName.toPrintableString()); } else { - String simpleName = fullTypeName; - if (simpleName.contains(".")) { - simpleName = simpleName.substring(simpleName.lastIndexOf('.') + 1); - } - writer.hilightSpecial(IdentifiersDeobfuscation.printNamespace(localData.constantsAvm2 != null, simpleName), HighlightSpecialType.TYPE_NAME, fullTypeName); + writer.hilightSpecial(IdentifiersDeobfuscation.printIdentifier(localData.constantsAvm2 != null, fullTypeName.getLast()), HighlightSpecialType.TYPE_NAME, fullTypeName.toPrintableString()); } - + return writer; } - + @Override public GraphTargetItem returnType() { return this; } - + @Override public boolean hasReturnValue() { return true; } - + @Override public String toString() { - return fullTypeName; + return fullTypeName.toString(); } - + @Override public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { return generator.generate(localData, this); diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/Helper.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/Helper.java index 5689b3a54..32be19f9d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/helpers/Helper.java +++ b/libsrc/ffdec_lib/src/com/jpexs/helpers/Helper.java @@ -1,1328 +1,1328 @@ -/* - * Copyright (C) 2010-2015 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.helpers; - -import com.jpexs.decompiler.flash.AppResources; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.helpers.Freed; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.TranslateStack; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.helpers.utf8.Utf8Helper; -import java.awt.Component; -import java.awt.Polygon; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.geom.Area; -import java.awt.image.BufferedImage; -import java.awt.image.DataBufferInt; -import java.io.BufferedOutputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.OutputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.BitSet; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Scanner; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.xml.bind.DatatypeConverter; - -/** - * Class with helper method - * - * @author JPEXS, Paolo Cancedda - */ -public class Helper { - - public static String newLine = System.getProperty("line.separator"); - - public static String hexData = "#hexdata"; - - public static String constants = "#constants"; - - public static String decompilationErrorAdd = null; - - private static final Map shapeCache = new HashMap<>(); - - private static final String[] hexStringCache; - - static { - hexStringCache = new String[256]; - for (int i = 0; i < hexStringCache.length; i++) { - hexStringCache[i] = String.format("%02x", i); - } - } - - /** - * Converts array of int values to string - * - * @param array Array of int values - * @return String representation of the array - */ - public static String intArrToString(int[] array) { - StringBuilder sb = new StringBuilder(); - intArrToStringBuilder(array, sb); - return sb.toString(); - } - - public static void intArrToStringBuilder(int[] array, StringBuilder sb) { - sb.append("["); - for (int i = 0; i < array.length; i++) { - if (i > 0) { - sb.append(","); - } - sb.append(array[i]); - } - sb.append("]"); - } - - /** - * Converts array of byte values to string - * - * @param array Array of byte values - * @return String representation of the array - */ - public static String byteArrToString(byte[] array) { - StringBuilder sb = new StringBuilder(); - sb.append("["); - for (int i = 0; i < array.length; i++) { - if (i > 0) { - sb.append(" "); - } - sb.append(hexStringCache[array[i] & 0xff]); - } - sb.append("]"); - return sb.toString(); - } - - /** - * Adds zeros to beginning of the number to fill specified length. Returns - * as string - * - * @param number Number as string - * @param length Length of new string - * @return Number with added zeros - */ - public static String padZeros(String number, int length) { - int count = length - number.length(); - for (int i = 0; i < count; i++) { - number = "0" + number; - } - return number; - } - - /** - * Formats specified address to four numbers xxxx - * (or five numbers when showing decimal addresses) - * - * @param number Address to format - * @return String representation of the address - */ - public static String formatAddress(long number) { - return formatAddress(number, Configuration.decimalAddress.get()); - } - - /** - * Formats specified address to four numbers xxxx - * (or five numbers when showing decimal addresses) - * - * @param number Address to format - * @param decimal Use decimal format - * @return String representation of the address - */ - public static String formatAddress(long number, boolean decimal) { - if (decimal) { - return String.format("%05d", number); - } - return String.format("%04x", number); - } - - /** - * Escapes string by adding backslashes - * - * @param s String to escape - * @return Escaped string - */ - public static String escapeString(String s) { - StringBuilder ret = new StringBuilder(s.length()); - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == '\n') { - ret.append("\\n"); - } else if (c == '\r') { - ret.append("\\r"); - } else if (c == '\t') { - ret.append("\\t"); - } else if (c == '\b') { - ret.append("\\b"); - } else if (c == '\f') { - ret.append("\\f"); - } else if (c == '\\') { - ret.append("\\\\"); - } else if (c < 32) { - ret.append("\\x").append(byteToHex((byte) c)); - } else { - ret.append(c); - } - } - - return ret.toString(); - } - - /** - * Escapes string by adding backslashes - * - * @param s String to escape - * @return Escaped string - */ - public static String escapeActionScriptString(String s) { - StringBuilder ret = new StringBuilder(s.length()); - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == '\n') { - ret.append("\\n"); - } else if (c == '\r') { - ret.append("\\r"); - } else if (c == '\t') { - ret.append("\\t"); - } else if (c == '\b') { - ret.append("\\b"); - } else if (c == '\f') { - ret.append("\\f"); - } else if (c == '\\') { - ret.append("\\\\"); - } else if (c == '"') { - ret.append("\\\""); - } else if (c == '\'') { - ret.append("\\'"); - } else if (c < 32) { - ret.append("\\x").append(byteToHex((byte) c)); - } else { - ret.append(c); - } - } - - return ret.toString(); - } - - /** - * Escapes string by adding backslashes - * - * @param s String to escape - * @return Escaped string - */ - public static String escapeJavaString(String s) { - StringBuilder ret = new StringBuilder(s.length()); - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == '\n') { - ret.append("\\n"); - } else if (c == '\r') { - ret.append("\\r"); - } else if (c == '\t') { - ret.append("\\t"); - } else if (c == '\b') { - ret.append("\\b"); - } else if (c == '\f') { - ret.append("\\f"); - } else if (c == '\\') { - ret.append("\\\\"); - } else if (c == '"') { - ret.append("\\\""); - } else if (c < 32) { - // \\x is not available in Java string, we should use \\u instead - ret.append("\\u00").append(byteToHex((byte) c)); - } else { - ret.append(c); - } - } - - return ret.toString(); - } - - /** - * Unescapes a string that contains standard Java escape sequences. - *
    - *
  • \b \f \n \r \t \" \' : - * BS, FF, NL, CR, TAB, double and single quote.
  • - *
  • \X \XX \XXX : Octal character - * specification (0 - 377, 0x00 - 0xFF).
  • - *
  • \uXXXX : Hexadecimal based Unicode - * character.
  • - *
- * - * @param st - * A string optionally containing standard java escape sequences. - * @return The translated string. - */ - public static String unescapeJavaString(String st) { - - StringBuilder sb = new StringBuilder(st.length()); - - for (int i = 0; i < st.length(); i++) { - char ch = st.charAt(i); - if (ch == '\\') { - char nextChar = (i == st.length() - 1) ? '\\' : st - .charAt(i + 1); - // Octal escape? - if (nextChar >= '0' && nextChar <= '7') { - String code = "" + nextChar; - i++; - if ((i < st.length() - 1) && st.charAt(i + 1) >= '0' - && st.charAt(i + 1) <= '7') { - code += st.charAt(i + 1); - i++; - if ((i < st.length() - 1) && st.charAt(i + 1) >= '0' - && st.charAt(i + 1) <= '7') { - code += st.charAt(i + 1); - i++; - } - } - sb.append((char) Integer.parseInt(code, 8)); - continue; - } - - switch (nextChar) { - case '\\': - ch = '\\'; - break; - case 'b': - ch = '\b'; - break; - case 'f': - ch = '\f'; - break; - case 'n': - ch = '\n'; - break; - case 'r': - ch = '\r'; - break; - case 't': - ch = '\t'; - break; - case '\"': - ch = '\"'; - break; - case '\'': - ch = '\''; - break; - // Hex Unicode: u???? - case 'u': - if (i >= st.length() - 5) { - ch = 'u'; - break; - } - int code = Integer.parseInt( - "" + st.charAt(i + 2) + st.charAt(i + 3) - + st.charAt(i + 4) + st.charAt(i + 5), 16); - sb.append(Character.toChars(code)); - i += 5; - continue; - } - - i++; - } - - sb.append(ch); - } - - return sb.toString(); - } - - public static String getValidHtmlId(String text) { - // ID and NAME tokens must begin with a letter ([A-Za-z]) and - // may be followed by any number of letters, digits ([0-9]), - // hyphens ("-"), underscores ("_"), colons (":"), and periods ("."). - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < text.length(); i++) { - char ch = text.charAt(i); - if ((ch >= 'a' && ch <= 'z') - || (ch >= 'A' && ch <= 'Z') - || (i > 0 && ((ch >= '0' && ch <= '9') - || ch == '-' || ch == '_' || ch == ':' || ch == '.'))) { - sb.append(ch); - } else { - sb.append('_'); - } - } - return sb.toString(); - } - - private final static String SPACES12 = " "; - - private final static String ZEROS8 = "00000000"; - - public static String formatHex(int value, int width) { - StringBuilder sb = new StringBuilder(); - sb.append(Integer.toHexString(value)); - if (width > sb.length()) { - sb.insert(0, ZEROS8, 0, width - sb.length()); - } - return sb.toString(); - } - - public static String formatInt(int value, int width) { - StringBuilder sb = new StringBuilder(); - sb.append(value); - if (width > sb.length()) { - sb.insert(0, SPACES12, 0, width - sb.length()); - } - return sb.toString(); - } - - public static String indent(int level, String ss, String indentStr) { - StringBuilder sb = new StringBuilder(); - for (int ii = 0; ii < level; ii++) { - sb.append(indentStr); - } - sb.append(ss); - return sb.toString(); - } - - public static String indentRows(int level, String ss, String indentStr) { - StringBuilder sb = new StringBuilder(); - for (int ii = 0; ii < level; ii++) { - sb.append(indentStr); - } - ss = ss.replaceAll("(\r\n|\r|\n)", "\r\n"); - ss = "\r\n" + ss; - String repl = "\r\n" + sb.toString(); - ss = ss.replace("\r\n", repl); - if (ss.endsWith(repl)) { - ss = ss.substring(0, ss.length() - sb.toString().length()); - } - ss = ss.substring(2); - return ss; - } - - public static String unindentRows(int prefixLineCount, int level, String text) { - StringBuilder sb = new StringBuilder(); - Scanner scanner = new Scanner(text); - String indentStr = ""; - for (int i = 0; i < level; i++) { - indentStr += Configuration.getCodeFormatting().indentString; - } - int indentLength = indentStr.length(); - for (int i = 0; i < prefixLineCount; i++) { - scanner.nextLine(); // ignore line - } - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - if (line.startsWith(indentStr)) { - sb.append(line.substring(indentLength)).append(Configuration.getCodeFormatting().newLineChars); - } else { - return sb.toString(); - } - } - return sb.toString(); - } - - public static int getLineCount(String s) { - if (s.endsWith("\r\n")) { - s = s.substring(0, s.length() - 2); - } else if (s.endsWith("\r")) { - s = s.substring(0, s.length() - 1); - } else if (s.endsWith("\n")) { - s = s.substring(0, s.length() - 1); - } - String[] parts = s.split("(\r\n|\r|\n)"); - return parts.length; - } - - public static String padZeros(long number, int length) { - String ret = Long.toString(number); - while (ret.length() < length) { - ret = "0" + ret; - } - return ret; - } - - public static String byteToHex(byte b) { - return hexStringCache[b & 0xff]; - } - - public static String byteArrayToHex(byte[] data) { - StringBuilder sb = new StringBuilder(data.length * 2); - for (byte b : data) { - sb.append(hexStringCache[b & 0xff]); - } - - return sb.toString(); - } - - public static String bytesToHexString(byte[] bytes) { - return bytesToHexString(bytes, 0); - } - - public static String bytesToHexString(byte[] bytes, int start) { - StringBuilder sb = new StringBuilder(); - if (start < bytes.length) { - for (int ii = start; ii < bytes.length; ii++) { - sb.append(formatHex(bytes[ii] & 0xff, 2)); - sb.append(' '); - } - sb.setLength(sb.length() - 1); - } - return sb.toString(); - } - - public static String bytesToHexString(int maxByteCountInString, byte[] bytes, int start) { - if (bytes.length - start <= maxByteCountInString) { - return bytesToHexString(bytes, start); - } - byte[] trailingBytes = new byte[maxByteCountInString / 2]; - byte[] headingBytes = new byte[maxByteCountInString - trailingBytes.length]; - System.arraycopy(bytes, start, headingBytes, 0, headingBytes.length); - int startOfTrailingBytes = bytes.length - trailingBytes.length; - System.arraycopy(bytes, startOfTrailingBytes, trailingBytes, 0, trailingBytes.length); - StringBuilder sb = new StringBuilder(); - sb.append(bytesToHexString(headingBytes, 0)); - if (trailingBytes.length > 0) { - sb.append(" ... "); - sb.append(bytesToHexString(trailingBytes, 0)); - } - return sb.toString(); - } - - public static String format(String str, int len) { - if (len <= str.length()) { - return str; - } - StringBuilder sb = new StringBuilder(str); - for (int ii = str.length(); ii < len; ii++) { - sb.append(' '); - } - return sb.toString(); - } - - public static String joinStrings(Iterable arr, String glue) { - StringBuilder ret = new StringBuilder(); - boolean first = true; - for (Object s : arr) { - if (!first) { - ret.append(glue); - } else { - first = false; - } - ret.append(s); - } - return ret.toString(); - } - - public static String joinStrings(String[] arr, String glue) { - StringBuilder ret = new StringBuilder(); - boolean first = true; - for (String s : arr) { - if (!first) { - ret.append(glue); - } else { - first = false; - } - ret.append(s); - } - return ret.toString(); - } - - public static String joinStrings(List arr, String formatString, String glue) { - StringBuilder ret = new StringBuilder(); - boolean first = true; - for (String s : arr) { - if (!first) { - ret.append(glue); - } else { - first = false; - } - ret.append(String.format(formatString, s)); - } - return ret.toString(); - } - - public static String joinStrings(String[] arr, String formatString, String glue) { - StringBuilder ret = new StringBuilder(); - boolean first = true; - for (String s : arr) { - if (!first) { - ret.append(glue); - } else { - first = false; - } - ret.append(String.format(formatString, s)); - } - return ret.toString(); - } - - @SuppressWarnings("unchecked") - public static E deepCopy(E o) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { - oos.writeObject(o); - oos.flush(); - } - E copy; - try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { - copy = (E) ois.readObject(); - } - return copy; - } catch (IOException | ClassNotFoundException ex) { - Logger.getLogger(Helper.class.getName()).log(Level.SEVERE, "Copy error", ex); - return null; - } - } - - public static List toList(Object... rest) { - List ret = new ArrayList<>(); - ret.addAll(Arrays.asList(rest)); - return ret; - } - - public static ByteArrayInputStream getInputStream(byte[]... data) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - try { - for (byte[] d : data) { - baos.write(d); - } - } catch (IOException iex) { - } - return new ByteArrayInputStream(baos.toByteArray()); - } - - public static byte[] readFile(String... file) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - for (String f : file) { - try (FileInputStream fis = new FileInputStream(f)) { - byte[] buf = new byte[4096]; - int cnt = 0; - while ((cnt = fis.read(buf)) > 0) { - baos.write(buf, 0, cnt); - } - } catch (IOException ex) { - Logger.getLogger(Helper.class.getName()).log(Level.SEVERE, null, ex); - } - } - return baos.toByteArray(); - } - - public static String readTextFile(String... file) { - byte[] data = readFile(file); - if (data.length > 1 && data[0] == (byte) 0xef && data[1] == (byte) 0xbb && data[2] == (byte) 0xbf) { - // remove UTF-8 BOM - return new String(data, 3, data.length - 3, Utf8Helper.charset); - } - - return new String(data, Utf8Helper.charset); - } - - public static byte[] readStream(InputStream is) { - if (is instanceof MemoryInputStream) { - return ((MemoryInputStream) is).getAllRead(); - } - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - copyStream(is, baos, Long.MAX_VALUE); - return baos.toByteArray(); - } - - public static void copyStream(InputStream is, OutputStream os, long maxLength) { - try { - final int bufSize = 4096; - byte[] buf = new byte[bufSize]; - int cnt = 0; - while ((cnt = is.read(buf)) > 0) { - os.write(buf, 0, cnt); - maxLength -= cnt; - - // last chunk is smaller - if (maxLength < bufSize) { - buf = new byte[(int) maxLength]; - } - } - } catch (IOException ex) { - // ignore - } - } - - public static void writeFile(String file, byte[]... data) { - try (FileOutputStream fos = new FileOutputStream(file)) { - for (byte[] d : data) { - fos.write(d); - } - } catch (IOException ex) { - // ignore - } - } - - public static String stackToString(TranslateStack stack, LocalData localData) throws InterruptedException { - String ret = "["; - for (int i = stack.size() - 1; i >= 0; i--) { - if (i < stack.size() - 1) { - ret += ", "; - } - ret += stack.get(i).toString(localData); - } - ret += "]"; - return ret; - } - - public static File fixDialogFile(File f) { - Pattern pat = Pattern.compile("\"([^\"]+)\""); - String name = f.getAbsolutePath(); - Matcher m = pat.matcher(name); - if (m.find()) { - f = new File(m.group(1)); - } - return f; - } - - private static final BitSet fileNameInvalidChars; - - private static final List invalidFilenamesParts; - - static { - BitSet toEncode = new BitSet(256); - - for (int i = 0; i < 32; i++) { - toEncode.set(i); - } - - toEncode.set('\\'); - toEncode.set('/'); - toEncode.set(':'); - toEncode.set('*'); - toEncode.set('?'); - toEncode.set('"'); - toEncode.set('<'); - toEncode.set('>'); - toEncode.set('|'); - - fileNameInvalidChars = toEncode; - - //windows reserved filenames: - invalidFilenamesParts = new ArrayList<>(); - invalidFilenamesParts.add("CON"); - invalidFilenamesParts.add("PRN"); - invalidFilenamesParts.add("AUX"); - invalidFilenamesParts.add("CLOCK$"); - invalidFilenamesParts.add("NUL"); - for (int i = 1; i <= 9; i++) { - invalidFilenamesParts.add("COM" + i); - invalidFilenamesParts.add("LPT" + i); - } - } - - public static String makeFileName(String str) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < str.length(); i++) { - int ch = (int) str.charAt(i); - if (ch < 256 && fileNameInvalidChars.get(ch)) { - sb.append("%").append(String.format("%02X", ch)); - } else { - sb.append((char) ch); - } - } - str = sb.toString(); - if (str.endsWith(" ")) { - str = str.substring(0, str.length() - 1) + "%20"; - } - if (str.endsWith(".")) { - str = str.substring(0, str.length() - 1) + "%2E"; - } - str = "." + str + "."; - for (String inv : invalidFilenamesParts) { - str = Pattern.compile("\\." + Pattern.quote(inv) + "\\.", Pattern.CASE_INSENSITIVE).matcher(str).replaceAll("._" + inv + "."); - } - str = str.substring(1, str.length() - 1); //remove dots - if (str.isEmpty()) { - str = "unnamed"; - } - return str; - } - - public static String strToHex(String s) { - byte[] bs = Utf8Helper.getBytes(s); - String sn = ""; - for (int i = 0; i < bs.length; i++) { - sn += "0x" + Integer.toHexString(bs[i] & 0xff) + " "; - } - return sn; - } - - public static void emptyObject(Object obj) { - Field[] fields = obj.getClass().getDeclaredFields(); - for (Field f : fields) { - if ((f.getModifiers() & Modifier.STATIC) == Modifier.STATIC - || f.getType().isPrimitive()) { - continue; - } - try { - f.setAccessible(true); - - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL); - - Object v = f.get(obj); - if (v != null) { - try { - if (v instanceof Collection) { - ((Collection) v).clear(); - } - if (v instanceof Component) { - if (((Component) v).getParent() != null) { - ((Component) v).getParent().remove((Component) v); - } - } - if (v instanceof Freed) { - Freed freed = ((Freed) v); - if (!freed.isFreeing()) { - ((Freed) v).free(); - } - } - } catch (Throwable t) { - } - - f.set(obj, null); - } - } catch (UnsupportedOperationException | SecurityException | IllegalArgumentException | IllegalAccessException | NoSuchFieldException ex) { - throw new Error(ex); - } - } - } - - public static String formatTimeSec(long timeMs) { - long timeS = timeMs / 1000; - timeMs %= 1000; - long timeM = timeS / 60; - timeS %= 60; - long timeH = timeM / 60; - timeM %= 60; - String timeStr = ""; - if (timeH > 0) { - timeStr += Helper.padZeros(timeH, 2) + ":"; - } - timeStr += Helper.padZeros(timeM, 2) + ":"; - timeStr += Helper.padZeros(timeS, 2) + "." + Helper.padZeros(timeMs, 3); - return timeStr; - } - - public static String formatFileSize(long fileSizeLong) { - double fileSize = fileSizeLong; - if (fileSize < 1024) { - return String.format("%d bytes", fileSizeLong); - } - fileSize /= 1024; - if (fileSize < 1024) { - return String.format("%.2f KB", fileSize); - } - fileSize /= 1024; - return String.format("%.2f MB", fileSize); - } - - public static void freeMem() { - Cache.clearAll(); - System.gc(); - } - - public static String formatTimeToText(int timeS) { - long timeM = timeS / 60; - timeS %= 60; - long timeH = timeM / 60; - timeM %= 60; - - String timeStr = ""; - String strAnd = AppResources.translate("timeFormat.and"); - String strHour = AppResources.translate("timeFormat.hour"); - String strHours = AppResources.translate("timeFormat.hours"); - String strMinute = AppResources.translate("timeFormat.minute"); - String strMinutes = AppResources.translate("timeFormat.minutes"); - String strSecond = AppResources.translate("timeFormat.second"); - String strSeconds = AppResources.translate("timeFormat.seconds"); - - if (timeH > 0) { - timeStr += timeH + " " + (timeH > 1 ? strHours : strHour); - } - if (timeM > 0) { - if (timeStr.length() > 0) { - timeStr += " " + strAnd + " "; - } - timeStr += timeM + " " + (timeM > 1 ? strMinutes : strMinute); - } - if (timeS > 0) { - if (timeStr.length() > 0) { - timeStr += " " + strAnd + " "; - } - timeStr += timeS + " " + (timeS > 1 ? strSeconds : strSecond); - } - - // (currently) used only in log, so no localization is required - return timeStr; - } - - public static GraphTextWriter byteArrayToHexWithHeader(GraphTextWriter writer, byte[] data) { - writer.appendNoHilight(hexData).newLine().newLine(); - return byteArrayToHex(writer, data, 8, 8, false, false); - } - - public static GraphTextWriter byteArrayToHex(GraphTextWriter writer, byte[] data, int bytesPerRow, int groupSize, boolean addChars, boolean showAddress) { - - /* // hex data from decompiled actions - Scanner scanner = new Scanner(srcWithHex); - while (scanner.hasNextLine()) { - String line = scanner.nextLine().trim(); - if (line.startsWith(";")) { - result.append(line.substring(1).trim()).append(nl); - } else { - result.append(";").append(line).append(nl); - } - }*/ - int length = data.length; - - int rowCount = length / bytesPerRow; - if (length % bytesPerRow > 0) { - rowCount++; - } - - long address = 0; - for (int row = 0; row < rowCount; row++) { - if (row > 0) { - writer.newLine(); - } - - if (showAddress) { - writer.appendNoHilight("0x" + String.format("%08x ", address)); - } - - for (int i = 0; i < bytesPerRow; i++) { - int idx = row * bytesPerRow + i; - if (length > idx) { - if (i > 0 && i % groupSize == 0) { - writer.appendNoHilight(" "); - } - writer.appendNoHilight(byteToHex(data[idx])).appendNoHilight(" "); - } else { - if (addChars) { - if (i > 0 && i % groupSize == 0) { - writer.appendNoHilight(" "); - } - writer.appendNoHilight(" "); - } - } - address += bytesPerRow; - } - - if (addChars) { - writer.appendNoHilight(" "); - for (int i = 0; i < bytesPerRow; i++) { - int idx = row * bytesPerRow + i; - if (length == idx) { - break; - } - if (i > 0 && i % groupSize == 0) { - writer.appendNoHilight(" "); - } - byte ch = data[idx]; - if (ch >= 0 && ch < 32) { - ch = '.'; - } - writer.appendNoHilight((char) ch + ""); - } - } - } - - writer.newLine(); - return writer; - } - - public static byte[] getBytesFromHexaText(String text) { - Scanner scanner = new Scanner(text); - scanner.nextLine(); // ignore first line - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - while (scanner.hasNextLine()) { - String line = scanner.nextLine().trim(); - if (line.startsWith(";")) { - continue; - } - line = line.replace(" ", ""); - for (int i = 0; i < line.length() / 2; i++) { - String hexStr = line.substring(i * 2, (i + 1) * 2); - byte b = (byte) Integer.parseInt(hexStr, 16); - baos.write(b); - } - } - byte[] data = baos.toByteArray(); - return data; - } - - public static List> getConstantPoolsFromText(String text) { - Scanner scanner = new Scanner(text); - scanner.nextLine(); // ignore first line - List> result = new ArrayList<>(); - List cPool = new ArrayList<>(); - result.add(cPool); - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - if (line.startsWith("---")) { - cPool = new ArrayList<>(); - result.add(cPool); - } - - String[] parts = line.split("\\|", 2); - if (parts.length >= 2) { - cPool.add(unescapeJavaString(parts[1])); - } - } - - return result; - } - - public static boolean contains(int[] array, int value) { - if (array == null) { - return false; - } - - for (int i : array) { - if (i == value) { - return true; - } - } - return false; - } - - public static void saveStream(InputStream is, File output) throws IOException { - byte[] buf = new byte[4096]; - int cnt; - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(output))) { - while ((cnt = is.read(buf)) > 0) { - fos.write(buf, 0, cnt); - fos.flush(); - } - } - } - - public static String getDecompilationSkippedComment() { - return "// " + AppResources.translate("decompilation.skipped"); - } - - public static void appendTimeoutComment(GraphTextWriter writer, int timeout) { - writer.appendNoHilight("/*").newLine(); - writer.appendNoHilight(" * ").appendNoHilight(AppResources.translate("decompilationError")).newLine(); - writer.appendNoHilight(" * ").appendNoHilight(MessageFormat.format(AppResources.translate("decompilationError.timeout"), Helper.formatTimeToText(timeout))).newLine(); - writer.appendNoHilight(" */").newLine(); - writer.appendNoHilight("throw new flash.errors.IllegalOperationError(\""). - appendNoHilight(AppResources.translate("decompilationError.timeout.description")). - appendNoHilight("\");").newLine(); - } - - public static void appendErrorComment(GraphTextWriter writer, Throwable ex) { - writer.appendNoHilight("/*").newLine(); - writer.appendNoHilight(" * ").appendNoHilight(AppResources.translate("decompilationError")).newLine(); - writer.appendNoHilight(" * ").appendNoHilight(AppResources.translate("decompilationError.obfuscated")).newLine(); - if (decompilationErrorAdd != null) { - writer.appendNoHilight(" * ").appendNoHilight(decompilationErrorAdd).newLine(); - } - writer.appendNoHilight(" * ").appendNoHilight(AppResources.translate("decompilationError.errorType")). - appendNoHilight(": " + ex.getClass().getSimpleName() + " (" + ex.getMessage() + ")").newLine(); - writer.appendNoHilight(" */").newLine(); - writer.appendNoHilight("throw new flash.errors.IllegalOperationError(\""). - appendNoHilight(AppResources.translate("decompilationError.error.description")). - appendNoHilight("\");").newLine(); - } - - public static String escapeHTML(String text) { - String[] from = new String[]{"&", "<", ">", "\"", "'", "/"}; - String[] to = new String[]{"&", "<", ">", """, "'", "/"}; - for (int i = 0; i < from.length; i++) { - text = text.replace(from[i], to[i]); - } - return text; - } - - public static String removeInvalidXMLCharacters(String text) { - StringBuilder sb = new StringBuilder(text.length()); - for (int i = 0; i < text.length(); i++) { - char ch = text.charAt(i); - if (ch > 31 || ch == 9 || ch == 10 || ch == 13) { - sb.append(ch); - } - } - - return sb.toString(); - } - - public static Shape imageToShapeOld(BufferedImage image) { - Area area = new Area(); - Rectangle rectangle = new Rectangle(); - int y1, y2; - int width = image.getWidth(); - int height = image.getHeight(); - - int[] imgData; - int type = image.getType(); - if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB) { - imgData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); - } else { - imgData = image.getRGB(0, 0, width, height, null, 0, width); - } - - BitSet bs = new BitSet(width * height); - bs.set(type); - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - int idx = width * y + x; - if ((imgData[idx] >>> 24) > 0) { - bs.set(idx); - } - } - } - - String key = byteArrayToBase64String(bs.toByteArray()); - if (shapeCache.containsKey(key)) { - return shapeCache.get(key); - } - - for (int x = 0; x < width; x++) { - y1 = Integer.MAX_VALUE; - y2 = -1; - for (int y = 0; y < height; y++) { - int rgb = imgData[width * y + x]; - rgb = rgb >>> 24; - if (rgb > 0) { - if (y1 == Integer.MAX_VALUE) { - y1 = y; - y2 = y; - } - if (y > (y2 + 1)) { - rectangle.setBounds(x, y1, 1, y2 - y1 + 1); - area.add(new Area(rectangle)); - y1 = y; - } - y2 = y; - } - } - if ((y2 - y1) >= 0) { - rectangle.setBounds(x, y1, 1, y2 - y1 + 1); - area.add(new Area(rectangle)); - } - } - - shapeCache.put(key, area); - return area; - } - - public static Shape imageToShape(BufferedImage image) { - Area area = new Area(); - int width = image.getWidth(); - int height = image.getHeight(); - - int[] imgData; - int type = image.getType(); - if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB) { - imgData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); - } else { - imgData = image.getRGB(0, 0, width, height, null, 0, width); - } - - BitSet bs = new BitSet(width * height); - bs.set(type); - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - int idx = width * y + x; - if ((imgData[idx] >>> 24) > 0) { - bs.set(idx); - } - } - } - - String key = byteArrayToBase64String(bs.toByteArray()); - if (shapeCache.containsKey(key)) { - return shapeCache.get(key); - } - - BitSet bsArea = new BitSet(width * height); - boolean modified = true; - - List leftCoordsX = new ArrayList<>(); - List leftCoordsY = new ArrayList<>(); - List rightCoordsX = new ArrayList<>(); - List rightCoordsY = new ArrayList<>(); - while (modified) { - modified = false; - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int idx = width * y + x; - if ((imgData[idx] >>> 24) > 0 && !bsArea.get(idx)) { - leftCoordsX.clear(); - leftCoordsY.clear(); - rightCoordsX.clear(); - rightCoordsY.clear(); - int leftX = x; - int rightX = findRight(imgData, x, y, width); - leftCoordsX.add(leftX); - leftCoordsY.add(y); - rightCoordsX.add(rightX); - rightCoordsY.add(y); - setBitSet(bsArea, leftX, rightX, y, width); - int y2 = y + 1; - while (y2 < height) { - leftCoordsX.add(leftX); - leftCoordsY.add(y2); - rightCoordsX.add(rightX); - rightCoordsY.add(y2); - - int leftX2 = findFirst(imgData, leftX, rightX, y2, width); - if (leftX2 == -1) { - break; - } - - int rightX2 = findRight(imgData, leftX2, y2, width); - - if (leftX2 != leftX) { - leftCoordsX.add(leftX2); - leftCoordsY.add(y2); - } - - if (rightX2 != rightX) { - rightCoordsX.add(rightX2); - rightCoordsY.add(y2); - } - - leftX = leftX2; - rightX = rightX2; - - setBitSet(bsArea, leftX, rightX, y2, width); - y2++; - } - - int cnt = leftCoordsX.size() + rightCoordsX.size(); - int[] xCoords = new int[cnt]; - int[] yCoords = new int[cnt]; - for (int i = 0; i < rightCoordsX.size(); i++) { - xCoords[i] = rightCoordsX.get(i); - yCoords[i] = rightCoordsY.get(i); - } - - int offset = rightCoordsX.size(); - for (int i = 0; i < leftCoordsX.size(); i++) { - int idx2 = leftCoordsX.size() - i - 1; - xCoords[i + offset] = leftCoordsX.get(idx2); - yCoords[i + offset] = leftCoordsY.get(idx2); - } - - Area area2 = new Area(new Polygon(xCoords, yCoords, xCoords.length)); - area.add(area2); - modified = true; - } - } - } - } - - shapeCache.put(key, area); - return area; - } - - private static void setBitSet(BitSet bitSet, int x1, int x2, int y, int width) { - int idx = width * y + x1; - int idx2 = width * y + x2; - for (; idx < idx2; idx++) { - bitSet.set(idx); - } - } - - private static int findFirst(int[] imgData, int x1, int x2, int y, int width) { - int idx = width * y + x1; - if ((imgData[idx] >>> 24) > 0) { - while (x1 > 0 && (imgData[idx - 1] >>> 24) > 0) { - x1--; - idx--; - } - return x1; - } - - int idx2 = width * y + x2; - for (; idx < idx2; idx++) { - if ((imgData[idx] >>> 24) > 0) { - return x1; - } - - x1++; - } - - return -1; - } - - private static int findRight(int[] imgData, int x, int y, int width) { - int result = x; - int idx = width * y + x; - while (result < width && (imgData[idx] >>> 24) > 0) { - result++; - idx++; - } - - return result; - } - - public static void clearShapeCache() { - shapeCache.clear(); - } - - public static String byteArrayToBase64String(byte[] data) { - return DatatypeConverter.printBase64Binary(data); - } - - /** - * Formats double value (removes .0 from end) - * - * @param d - * @return String - */ - public static String doubleStr(double d) { - String ret = Double.toString(d); - if (ret.endsWith(".0")) { - ret = ret.substring(0, ret.length() - 2); - } - return ret; - } - - public static String byteCountStr(long bytes, boolean si) { - int unit = si ? 1000 : 1024; - if (bytes < unit) { - return bytes + " B"; - } - int exp = (int) (Math.log(bytes) / Math.log(unit)); - String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i"); - return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); - } -} +/* + * Copyright (C) 2010-2015 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.helpers; + +import com.jpexs.decompiler.flash.AppResources; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.helpers.Freed; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.TranslateStack; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.utf8.Utf8Helper; +import java.awt.Component; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.Area; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.xml.bind.DatatypeConverter; + +/** + * Class with helper method + * + * @author JPEXS, Paolo Cancedda + */ +public class Helper { + + public static String newLine = System.getProperty("line.separator"); + + public static String hexData = "#hexdata"; + + public static String constants = "#constants"; + + public static String decompilationErrorAdd = null; + + private static final Map shapeCache = new HashMap<>(); + + private static final String[] hexStringCache; + + static { + hexStringCache = new String[256]; + for (int i = 0; i < hexStringCache.length; i++) { + hexStringCache[i] = String.format("%02x", i); + } + } + + /** + * Converts array of int values to string + * + * @param array Array of int values + * @return String representation of the array + */ + public static String intArrToString(int[] array) { + StringBuilder sb = new StringBuilder(); + intArrToStringBuilder(array, sb); + return sb.toString(); + } + + public static void intArrToStringBuilder(int[] array, StringBuilder sb) { + sb.append("["); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + sb.append(","); + } + sb.append(array[i]); + } + sb.append("]"); + } + + /** + * Converts array of byte values to string + * + * @param array Array of byte values + * @return String representation of the array + */ + public static String byteArrToString(byte[] array) { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + sb.append(" "); + } + sb.append(hexStringCache[array[i] & 0xff]); + } + sb.append("]"); + return sb.toString(); + } + + /** + * Adds zeros to beginning of the number to fill specified length. Returns + * as string + * + * @param number Number as string + * @param length Length of new string + * @return Number with added zeros + */ + public static String padZeros(String number, int length) { + int count = length - number.length(); + for (int i = 0; i < count; i++) { + number = "0" + number; + } + return number; + } + + /** + * Formats specified address to four numbers xxxx + * (or five numbers when showing decimal addresses) + * + * @param number Address to format + * @return String representation of the address + */ + public static String formatAddress(long number) { + return formatAddress(number, Configuration.decimalAddress.get()); + } + + /** + * Formats specified address to four numbers xxxx + * (or five numbers when showing decimal addresses) + * + * @param number Address to format + * @param decimal Use decimal format + * @return String representation of the address + */ + public static String formatAddress(long number, boolean decimal) { + if (decimal) { + return String.format("%05d", number); + } + return String.format("%04x", number); + } + + /** + * Escapes string by adding backslashes + * + * @param s String to escape + * @return Escaped string + */ + public static String escapeString(String s) { + StringBuilder ret = new StringBuilder(s.length()); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\n') { + ret.append("\\n"); + } else if (c == '\r') { + ret.append("\\r"); + } else if (c == '\t') { + ret.append("\\t"); + } else if (c == '\b') { + ret.append("\\b"); + } else if (c == '\f') { + ret.append("\\f"); + } else if (c == '\\') { + ret.append("\\\\"); + } else if (c < 32) { + ret.append("\\x").append(byteToHex((byte) c)); + } else { + ret.append(c); + } + } + + return ret.toString(); + } + + /** + * Escapes string by adding backslashes + * + * @param s String to escape + * @return Escaped string + */ + public static String escapeActionScriptString(String s) { + StringBuilder ret = new StringBuilder(s.length()); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\n') { + ret.append("\\n"); + } else if (c == '\r') { + ret.append("\\r"); + } else if (c == '\t') { + ret.append("\\t"); + } else if (c == '\b') { + ret.append("\\b"); + } else if (c == '\f') { + ret.append("\\f"); + } else if (c == '\\') { + ret.append("\\\\"); + } else if (c == '"') { + ret.append("\\\""); + } else if (c == '\'') { + ret.append("\\'"); + } else if (c < 32) { + ret.append("\\x").append(byteToHex((byte) c)); + } else { + ret.append(c); + } + } + + return ret.toString(); + } + + /** + * Escapes string by adding backslashes + * + * @param s String to escape + * @return Escaped string + */ + public static String escapeJavaString(String s) { + StringBuilder ret = new StringBuilder(s.length()); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\n') { + ret.append("\\n"); + } else if (c == '\r') { + ret.append("\\r"); + } else if (c == '\t') { + ret.append("\\t"); + } else if (c == '\b') { + ret.append("\\b"); + } else if (c == '\f') { + ret.append("\\f"); + } else if (c == '\\') { + ret.append("\\\\"); + } else if (c == '"') { + ret.append("\\\""); + } else if (c < 32) { + // \\x is not available in Java string, we should use \\u instead + ret.append("\\u00").append(byteToHex((byte) c)); + } else { + ret.append(c); + } + } + + return ret.toString(); + } + + /** + * Unescapes a string that contains standard Java escape sequences. + *
    + *
  • \b \f \n \r \t \" \' : + * BS, FF, NL, CR, TAB, double and single quote.
  • + *
  • \X \XX \XXX : Octal character + * specification (0 - 377, 0x00 - 0xFF).
  • + *
  • \uXXXX : Hexadecimal based Unicode + * character.
  • + *
+ * + * @param st + * A string optionally containing standard java escape sequences. + * @return The translated string. + */ + public static String unescapeJavaString(String st) { + + StringBuilder sb = new StringBuilder(st.length()); + + for (int i = 0; i < st.length(); i++) { + char ch = st.charAt(i); + if (ch == '\\') { + char nextChar = (i == st.length() - 1) ? '\\' : st + .charAt(i + 1); + // Octal escape? + if (nextChar >= '0' && nextChar <= '7') { + String code = "" + nextChar; + i++; + if ((i < st.length() - 1) && st.charAt(i + 1) >= '0' + && st.charAt(i + 1) <= '7') { + code += st.charAt(i + 1); + i++; + if ((i < st.length() - 1) && st.charAt(i + 1) >= '0' + && st.charAt(i + 1) <= '7') { + code += st.charAt(i + 1); + i++; + } + } + sb.append((char) Integer.parseInt(code, 8)); + continue; + } + + switch (nextChar) { + case '\\': + ch = '\\'; + break; + case 'b': + ch = '\b'; + break; + case 'f': + ch = '\f'; + break; + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 't': + ch = '\t'; + break; + case '\"': + ch = '\"'; + break; + case '\'': + ch = '\''; + break; + // Hex Unicode: u???? + case 'u': + if (i >= st.length() - 5) { + ch = 'u'; + break; + } + int code = Integer.parseInt( + "" + st.charAt(i + 2) + st.charAt(i + 3) + + st.charAt(i + 4) + st.charAt(i + 5), 16); + sb.append(Character.toChars(code)); + i += 5; + continue; + } + + i++; + } + + sb.append(ch); + } + + return sb.toString(); + } + + public static String getValidHtmlId(String text) { + // ID and NAME tokens must begin with a letter ([A-Za-z]) and + // may be followed by any number of letters, digits ([0-9]), + // hyphens ("-"), underscores ("_"), colons (":"), and periods ("."). + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < text.length(); i++) { + char ch = text.charAt(i); + if ((ch >= 'a' && ch <= 'z') + || (ch >= 'A' && ch <= 'Z') + || (i > 0 && ((ch >= '0' && ch <= '9') + || ch == '-' || ch == '_' || ch == ':' || ch == '.'))) { + sb.append(ch); + } else { + sb.append('_'); + } + } + return sb.toString(); + } + + private final static String SPACES12 = " "; + + private final static String ZEROS8 = "00000000"; + + public static String formatHex(int value, int width) { + StringBuilder sb = new StringBuilder(); + sb.append(Integer.toHexString(value)); + if (width > sb.length()) { + sb.insert(0, ZEROS8, 0, width - sb.length()); + } + return sb.toString(); + } + + public static String formatInt(int value, int width) { + StringBuilder sb = new StringBuilder(); + sb.append(value); + if (width > sb.length()) { + sb.insert(0, SPACES12, 0, width - sb.length()); + } + return sb.toString(); + } + + public static String indent(int level, String ss, String indentStr) { + StringBuilder sb = new StringBuilder(); + for (int ii = 0; ii < level; ii++) { + sb.append(indentStr); + } + sb.append(ss); + return sb.toString(); + } + + public static String indentRows(int level, String ss, String indentStr) { + StringBuilder sb = new StringBuilder(); + for (int ii = 0; ii < level; ii++) { + sb.append(indentStr); + } + ss = ss.replaceAll("(\r\n|\r|\n)", "\r\n"); + ss = "\r\n" + ss; + String repl = "\r\n" + sb.toString(); + ss = ss.replace("\r\n", repl); + if (ss.endsWith(repl)) { + ss = ss.substring(0, ss.length() - sb.toString().length()); + } + ss = ss.substring(2); + return ss; + } + + public static String unindentRows(int prefixLineCount, int level, String text) { + StringBuilder sb = new StringBuilder(); + Scanner scanner = new Scanner(text); + String indentStr = ""; + for (int i = 0; i < level; i++) { + indentStr += Configuration.getCodeFormatting().indentString; + } + int indentLength = indentStr.length(); + for (int i = 0; i < prefixLineCount; i++) { + scanner.nextLine(); // ignore line + } + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + if (line.startsWith(indentStr)) { + sb.append(line.substring(indentLength)).append(Configuration.getCodeFormatting().newLineChars); + } else { + return sb.toString(); + } + } + return sb.toString(); + } + + public static int getLineCount(String s) { + if (s.endsWith("\r\n")) { + s = s.substring(0, s.length() - 2); + } else if (s.endsWith("\r")) { + s = s.substring(0, s.length() - 1); + } else if (s.endsWith("\n")) { + s = s.substring(0, s.length() - 1); + } + String[] parts = s.split("(\r\n|\r|\n)"); + return parts.length; + } + + public static String padZeros(long number, int length) { + String ret = Long.toString(number); + while (ret.length() < length) { + ret = "0" + ret; + } + return ret; + } + + public static String byteToHex(byte b) { + return hexStringCache[b & 0xff]; + } + + public static String byteArrayToHex(byte[] data) { + StringBuilder sb = new StringBuilder(data.length * 2); + for (byte b : data) { + sb.append(hexStringCache[b & 0xff]); + } + + return sb.toString(); + } + + public static String bytesToHexString(byte[] bytes) { + return bytesToHexString(bytes, 0); + } + + public static String bytesToHexString(byte[] bytes, int start) { + StringBuilder sb = new StringBuilder(); + if (start < bytes.length) { + for (int ii = start; ii < bytes.length; ii++) { + sb.append(formatHex(bytes[ii] & 0xff, 2)); + sb.append(' '); + } + sb.setLength(sb.length() - 1); + } + return sb.toString(); + } + + public static String bytesToHexString(int maxByteCountInString, byte[] bytes, int start) { + if (bytes.length - start <= maxByteCountInString) { + return bytesToHexString(bytes, start); + } + byte[] trailingBytes = new byte[maxByteCountInString / 2]; + byte[] headingBytes = new byte[maxByteCountInString - trailingBytes.length]; + System.arraycopy(bytes, start, headingBytes, 0, headingBytes.length); + int startOfTrailingBytes = bytes.length - trailingBytes.length; + System.arraycopy(bytes, startOfTrailingBytes, trailingBytes, 0, trailingBytes.length); + StringBuilder sb = new StringBuilder(); + sb.append(bytesToHexString(headingBytes, 0)); + if (trailingBytes.length > 0) { + sb.append(" ... "); + sb.append(bytesToHexString(trailingBytes, 0)); + } + return sb.toString(); + } + + public static String format(String str, int len) { + if (len <= str.length()) { + return str; + } + StringBuilder sb = new StringBuilder(str); + for (int ii = str.length(); ii < len; ii++) { + sb.append(' '); + } + return sb.toString(); + } + + public static String joinStrings(Iterable arr, String glue) { + StringBuilder ret = new StringBuilder(); + boolean first = true; + for (Object s : arr) { + if (!first) { + ret.append(glue); + } else { + first = false; + } + ret.append(s); + } + return ret.toString(); + } + + public static String joinStrings(String[] arr, String glue) { + StringBuilder ret = new StringBuilder(); + boolean first = true; + for (String s : arr) { + if (!first) { + ret.append(glue); + } else { + first = false; + } + ret.append(s); + } + return ret.toString(); + } + + public static String joinStrings(List arr, String formatString, String glue) { + StringBuilder ret = new StringBuilder(); + boolean first = true; + for (String s : arr) { + if (!first) { + ret.append(glue); + } else { + first = false; + } + ret.append(String.format(formatString, s)); + } + return ret.toString(); + } + + public static String joinStrings(String[] arr, String formatString, String glue) { + StringBuilder ret = new StringBuilder(); + boolean first = true; + for (String s : arr) { + if (!first) { + ret.append(glue); + } else { + first = false; + } + ret.append(String.format(formatString, s)); + } + return ret.toString(); + } + + @SuppressWarnings("unchecked") + public static E deepCopy(E o) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(o); + oos.flush(); + } + E copy; + try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { + copy = (E) ois.readObject(); + } + return copy; + } catch (IOException | ClassNotFoundException ex) { + Logger.getLogger(Helper.class.getName()).log(Level.SEVERE, "Copy error", ex); + return null; + } + } + + public static List toList(Object... rest) { + List ret = new ArrayList<>(); + ret.addAll(Arrays.asList(rest)); + return ret; + } + + public static ByteArrayInputStream getInputStream(byte[]... data) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + try { + for (byte[] d : data) { + baos.write(d); + } + } catch (IOException iex) { + } + return new ByteArrayInputStream(baos.toByteArray()); + } + + public static byte[] readFile(String... file) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + for (String f : file) { + try (FileInputStream fis = new FileInputStream(f)) { + byte[] buf = new byte[4096]; + int cnt = 0; + while ((cnt = fis.read(buf)) > 0) { + baos.write(buf, 0, cnt); + } + } catch (IOException ex) { + Logger.getLogger(Helper.class.getName()).log(Level.SEVERE, null, ex); + } + } + return baos.toByteArray(); + } + + public static String readTextFile(String... file) { + byte[] data = readFile(file); + if (data.length > 1 && data[0] == (byte) 0xef && data[1] == (byte) 0xbb && data[2] == (byte) 0xbf) { + // remove UTF-8 BOM + return new String(data, 3, data.length - 3, Utf8Helper.charset); + } + + return new String(data, Utf8Helper.charset); + } + + public static byte[] readStream(InputStream is) { + if (is instanceof MemoryInputStream) { + return ((MemoryInputStream) is).getAllRead(); + } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + copyStream(is, baos, Long.MAX_VALUE); + return baos.toByteArray(); + } + + public static void copyStream(InputStream is, OutputStream os, long maxLength) { + try { + final int bufSize = 4096; + byte[] buf = new byte[bufSize]; + int cnt = 0; + while ((cnt = is.read(buf)) > 0) { + os.write(buf, 0, cnt); + maxLength -= cnt; + + // last chunk is smaller + if (maxLength < bufSize) { + buf = new byte[(int) maxLength]; + } + } + } catch (IOException ex) { + // ignore + } + } + + public static void writeFile(String file, byte[]... data) { + try (FileOutputStream fos = new FileOutputStream(file)) { + for (byte[] d : data) { + fos.write(d); + } + } catch (IOException ex) { + // ignore + } + } + + public static String stackToString(TranslateStack stack, LocalData localData) throws InterruptedException { + String ret = "["; + for (int i = stack.size() - 1; i >= 0; i--) { + if (i < stack.size() - 1) { + ret += ", "; + } + ret += stack.get(i).toString(localData); + } + ret += "]"; + return ret; + } + + public static File fixDialogFile(File f) { + Pattern pat = Pattern.compile("\"([^\"]+)\""); + String name = f.getAbsolutePath(); + Matcher m = pat.matcher(name); + if (m.find()) { + f = new File(m.group(1)); + } + return f; + } + + private static final BitSet fileNameInvalidChars; + + private static final List invalidFilenamesParts; + + static { + BitSet toEncode = new BitSet(256); + + for (int i = 0; i < 32; i++) { + toEncode.set(i); + } + + toEncode.set('\\'); + toEncode.set('/'); + toEncode.set(':'); + toEncode.set('*'); + toEncode.set('?'); + toEncode.set('"'); + toEncode.set('<'); + toEncode.set('>'); + toEncode.set('|'); + + fileNameInvalidChars = toEncode; + + //windows reserved filenames: + invalidFilenamesParts = new ArrayList<>(); + invalidFilenamesParts.add("CON"); + invalidFilenamesParts.add("PRN"); + invalidFilenamesParts.add("AUX"); + invalidFilenamesParts.add("CLOCK$"); + invalidFilenamesParts.add("NUL"); + for (int i = 1; i <= 9; i++) { + invalidFilenamesParts.add("COM" + i); + invalidFilenamesParts.add("LPT" + i); + } + } + + public static String makeFileName(String str) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + int ch = (int) str.charAt(i); + if (ch < 256 && fileNameInvalidChars.get(ch)) { + sb.append("%").append(String.format("%02X", ch)); + } else { + sb.append((char) ch); + } + } + str = sb.toString(); + if (str.endsWith(" ")) { + str = str.substring(0, str.length() - 1) + "%20"; + } + if (str.endsWith(".")) { + str = str.substring(0, str.length() - 1) + "%2E"; + } + str = "." + str + "."; + for (String inv : invalidFilenamesParts) { + str = Pattern.compile("\\." + Pattern.quote(inv) + "\\.", Pattern.CASE_INSENSITIVE).matcher(str).replaceAll("._" + inv + "."); + } + str = str.substring(1, str.length() - 1); //remove dots + if (str.isEmpty()) { + str = "unnamed"; + } + return str; + } + + public static String strToHex(String s) { + byte[] bs = Utf8Helper.getBytes(s); + String sn = ""; + for (int i = 0; i < bs.length; i++) { + sn += "0x" + Integer.toHexString(bs[i] & 0xff) + " "; + } + return sn; + } + + public static void emptyObject(Object obj) { + Field[] fields = obj.getClass().getDeclaredFields(); + for (Field f : fields) { + if ((f.getModifiers() & Modifier.STATIC) == Modifier.STATIC + || f.getType().isPrimitive()) { + continue; + } + try { + f.setAccessible(true); + + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL); + + Object v = f.get(obj); + if (v != null) { + try { + if (v instanceof Collection) { + ((Collection) v).clear(); + } + if (v instanceof Component) { + if (((Component) v).getParent() != null) { + ((Component) v).getParent().remove((Component) v); + } + } + if (v instanceof Freed) { + Freed freed = ((Freed) v); + if (!freed.isFreeing()) { + ((Freed) v).free(); + } + } + } catch (Throwable t) { + } + + f.set(obj, null); + } + } catch (UnsupportedOperationException | SecurityException | IllegalArgumentException | IllegalAccessException | NoSuchFieldException ex) { + throw new Error(ex); + } + } + } + + public static String formatTimeSec(long timeMs) { + long timeS = timeMs / 1000; + timeMs %= 1000; + long timeM = timeS / 60; + timeS %= 60; + long timeH = timeM / 60; + timeM %= 60; + String timeStr = ""; + if (timeH > 0) { + timeStr += Helper.padZeros(timeH, 2) + ":"; + } + timeStr += Helper.padZeros(timeM, 2) + ":"; + timeStr += Helper.padZeros(timeS, 2) + "." + Helper.padZeros(timeMs, 3); + return timeStr; + } + + public static String formatFileSize(long fileSizeLong) { + double fileSize = fileSizeLong; + if (fileSize < 1024) { + return String.format("%d bytes", fileSizeLong); + } + fileSize /= 1024; + if (fileSize < 1024) { + return String.format("%.2f KB", fileSize); + } + fileSize /= 1024; + return String.format("%.2f MB", fileSize); + } + + public static void freeMem() { + Cache.clearAll(); + System.gc(); + } + + public static String formatTimeToText(int timeS) { + long timeM = timeS / 60; + timeS %= 60; + long timeH = timeM / 60; + timeM %= 60; + + String timeStr = ""; + String strAnd = AppResources.translate("timeFormat.and"); + String strHour = AppResources.translate("timeFormat.hour"); + String strHours = AppResources.translate("timeFormat.hours"); + String strMinute = AppResources.translate("timeFormat.minute"); + String strMinutes = AppResources.translate("timeFormat.minutes"); + String strSecond = AppResources.translate("timeFormat.second"); + String strSeconds = AppResources.translate("timeFormat.seconds"); + + if (timeH > 0) { + timeStr += timeH + " " + (timeH > 1 ? strHours : strHour); + } + if (timeM > 0) { + if (timeStr.length() > 0) { + timeStr += " " + strAnd + " "; + } + timeStr += timeM + " " + (timeM > 1 ? strMinutes : strMinute); + } + if (timeS > 0) { + if (timeStr.length() > 0) { + timeStr += " " + strAnd + " "; + } + timeStr += timeS + " " + (timeS > 1 ? strSeconds : strSecond); + } + + // (currently) used only in log, so no localization is required + return timeStr; + } + + public static GraphTextWriter byteArrayToHexWithHeader(GraphTextWriter writer, byte[] data) { + writer.appendNoHilight(hexData).newLine().newLine(); + return byteArrayToHex(writer, data, 8, 8, false, false); + } + + public static GraphTextWriter byteArrayToHex(GraphTextWriter writer, byte[] data, int bytesPerRow, int groupSize, boolean addChars, boolean showAddress) { + + /* // hex data from decompiled actions + Scanner scanner = new Scanner(srcWithHex); + while (scanner.hasNextLine()) { + String line = scanner.nextLine().trim(); + if (line.startsWith(";")) { + result.append(line.substring(1).trim()).append(nl); + } else { + result.append(";").append(line).append(nl); + } + }*/ + int length = data.length; + + int rowCount = length / bytesPerRow; + if (length % bytesPerRow > 0) { + rowCount++; + } + + long address = 0; + for (int row = 0; row < rowCount; row++) { + if (row > 0) { + writer.newLine(); + } + + if (showAddress) { + writer.appendNoHilight("0x" + String.format("%08x ", address)); + } + + for (int i = 0; i < bytesPerRow; i++) { + int idx = row * bytesPerRow + i; + if (length > idx) { + if (i > 0 && i % groupSize == 0) { + writer.appendNoHilight(" "); + } + writer.appendNoHilight(byteToHex(data[idx])).appendNoHilight(" "); + } else { + if (addChars) { + if (i > 0 && i % groupSize == 0) { + writer.appendNoHilight(" "); + } + writer.appendNoHilight(" "); + } + } + address += bytesPerRow; + } + + if (addChars) { + writer.appendNoHilight(" "); + for (int i = 0; i < bytesPerRow; i++) { + int idx = row * bytesPerRow + i; + if (length == idx) { + break; + } + if (i > 0 && i % groupSize == 0) { + writer.appendNoHilight(" "); + } + byte ch = data[idx]; + if (ch >= 0 && ch < 32) { + ch = '.'; + } + writer.appendNoHilight((char) ch + ""); + } + } + } + + writer.newLine(); + return writer; + } + + public static byte[] getBytesFromHexaText(String text) { + Scanner scanner = new Scanner(text); + scanner.nextLine(); // ignore first line + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + while (scanner.hasNextLine()) { + String line = scanner.nextLine().trim(); + if (line.startsWith(";")) { + continue; + } + line = line.replace(" ", ""); + for (int i = 0; i < line.length() / 2; i++) { + String hexStr = line.substring(i * 2, (i + 1) * 2); + byte b = (byte) Integer.parseInt(hexStr, 16); + baos.write(b); + } + } + byte[] data = baos.toByteArray(); + return data; + } + + public static List> getConstantPoolsFromText(String text) { + Scanner scanner = new Scanner(text); + scanner.nextLine(); // ignore first line + List> result = new ArrayList<>(); + List cPool = new ArrayList<>(); + result.add(cPool); + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + if (line.startsWith("---")) { + cPool = new ArrayList<>(); + result.add(cPool); + } + + String[] parts = line.split("\\|", 2); + if (parts.length >= 2) { + cPool.add(unescapeJavaString(parts[1])); + } + } + + return result; + } + + public static boolean contains(int[] array, int value) { + if (array == null) { + return false; + } + + for (int i : array) { + if (i == value) { + return true; + } + } + return false; + } + + public static void saveStream(InputStream is, File output) throws IOException { + byte[] buf = new byte[4096]; + int cnt; + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(output))) { + while ((cnt = is.read(buf)) > 0) { + fos.write(buf, 0, cnt); + fos.flush(); + } + } + } + + public static String getDecompilationSkippedComment() { + return "// " + AppResources.translate("decompilation.skipped"); + } + + public static void appendTimeoutComment(GraphTextWriter writer, int timeout) { + writer.appendNoHilight("/*").newLine(); + writer.appendNoHilight(" * ").appendNoHilight(AppResources.translate("decompilationError")).newLine(); + writer.appendNoHilight(" * ").appendNoHilight(MessageFormat.format(AppResources.translate("decompilationError.timeout"), Helper.formatTimeToText(timeout))).newLine(); + writer.appendNoHilight(" */").newLine(); + writer.appendNoHilight("throw new flash.errors.IllegalOperationError(\""). + appendNoHilight(AppResources.translate("decompilationError.timeout.description")). + appendNoHilight("\");").newLine(); + } + + public static void appendErrorComment(GraphTextWriter writer, Throwable ex) { + writer.appendNoHilight("/*").newLine(); + writer.appendNoHilight(" * ").appendNoHilight(AppResources.translate("decompilationError")).newLine(); + writer.appendNoHilight(" * ").appendNoHilight(AppResources.translate("decompilationError.obfuscated")).newLine(); + if (decompilationErrorAdd != null) { + writer.appendNoHilight(" * ").appendNoHilight(decompilationErrorAdd).newLine(); + } + writer.appendNoHilight(" * ").appendNoHilight(AppResources.translate("decompilationError.errorType")). + appendNoHilight(": " + ex.getClass().getSimpleName() + " (" + ex.getMessage() + ")").newLine(); + writer.appendNoHilight(" */").newLine(); + writer.appendNoHilight("throw new flash.errors.IllegalOperationError(\""). + appendNoHilight(AppResources.translate("decompilationError.error.description")). + appendNoHilight("\");").newLine(); + } + + public static String escapeHTML(String text) { + String[] from = new String[]{"&", "<", ">", "\"", "'", "/"}; + String[] to = new String[]{"&", "<", ">", """, "'", "/"}; + for (int i = 0; i < from.length; i++) { + text = text.replace(from[i], to[i]); + } + return text; + } + + public static String removeInvalidXMLCharacters(String text) { + StringBuilder sb = new StringBuilder(text.length()); + for (int i = 0; i < text.length(); i++) { + char ch = text.charAt(i); + if (ch > 31 || ch == 9 || ch == 10 || ch == 13) { + sb.append(ch); + } + } + + return sb.toString(); + } + + public static Shape imageToShapeOld(BufferedImage image) { + Area area = new Area(); + Rectangle rectangle = new Rectangle(); + int y1, y2; + int width = image.getWidth(); + int height = image.getHeight(); + + int[] imgData; + int type = image.getType(); + if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB) { + imgData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + } else { + imgData = image.getRGB(0, 0, width, height, null, 0, width); + } + + BitSet bs = new BitSet(width * height); + bs.set(type); + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + int idx = width * y + x; + if ((imgData[idx] >>> 24) > 0) { + bs.set(idx); + } + } + } + + String key = byteArrayToBase64String(bs.toByteArray()); + if (shapeCache.containsKey(key)) { + return shapeCache.get(key); + } + + for (int x = 0; x < width; x++) { + y1 = Integer.MAX_VALUE; + y2 = -1; + for (int y = 0; y < height; y++) { + int rgb = imgData[width * y + x]; + rgb = rgb >>> 24; + if (rgb > 0) { + if (y1 == Integer.MAX_VALUE) { + y1 = y; + y2 = y; + } + if (y > (y2 + 1)) { + rectangle.setBounds(x, y1, 1, y2 - y1 + 1); + area.add(new Area(rectangle)); + y1 = y; + } + y2 = y; + } + } + if ((y2 - y1) >= 0) { + rectangle.setBounds(x, y1, 1, y2 - y1 + 1); + area.add(new Area(rectangle)); + } + } + + shapeCache.put(key, area); + return area; + } + + public static Shape imageToShape(BufferedImage image) { + Area area = new Area(); + int width = image.getWidth(); + int height = image.getHeight(); + + int[] imgData; + int type = image.getType(); + if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB) { + imgData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + } else { + imgData = image.getRGB(0, 0, width, height, null, 0, width); + } + + BitSet bs = new BitSet(width * height); + bs.set(type); + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + int idx = width * y + x; + if ((imgData[idx] >>> 24) > 0) { + bs.set(idx); + } + } + } + + String key = byteArrayToBase64String(bs.toByteArray()); + if (shapeCache.containsKey(key)) { + return shapeCache.get(key); + } + + BitSet bsArea = new BitSet(width * height); + boolean modified = true; + + List leftCoordsX = new ArrayList<>(); + List leftCoordsY = new ArrayList<>(); + List rightCoordsX = new ArrayList<>(); + List rightCoordsY = new ArrayList<>(); + while (modified) { + modified = false; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int idx = width * y + x; + if ((imgData[idx] >>> 24) > 0 && !bsArea.get(idx)) { + leftCoordsX.clear(); + leftCoordsY.clear(); + rightCoordsX.clear(); + rightCoordsY.clear(); + int leftX = x; + int rightX = findRight(imgData, x, y, width); + leftCoordsX.add(leftX); + leftCoordsY.add(y); + rightCoordsX.add(rightX); + rightCoordsY.add(y); + setBitSet(bsArea, leftX, rightX, y, width); + int y2 = y + 1; + while (y2 < height) { + leftCoordsX.add(leftX); + leftCoordsY.add(y2); + rightCoordsX.add(rightX); + rightCoordsY.add(y2); + + int leftX2 = findFirst(imgData, leftX, rightX, y2, width); + if (leftX2 == -1) { + break; + } + + int rightX2 = findRight(imgData, leftX2, y2, width); + + if (leftX2 != leftX) { + leftCoordsX.add(leftX2); + leftCoordsY.add(y2); + } + + if (rightX2 != rightX) { + rightCoordsX.add(rightX2); + rightCoordsY.add(y2); + } + + leftX = leftX2; + rightX = rightX2; + + setBitSet(bsArea, leftX, rightX, y2, width); + y2++; + } + + int cnt = leftCoordsX.size() + rightCoordsX.size(); + int[] xCoords = new int[cnt]; + int[] yCoords = new int[cnt]; + for (int i = 0; i < rightCoordsX.size(); i++) { + xCoords[i] = rightCoordsX.get(i); + yCoords[i] = rightCoordsY.get(i); + } + + int offset = rightCoordsX.size(); + for (int i = 0; i < leftCoordsX.size(); i++) { + int idx2 = leftCoordsX.size() - i - 1; + xCoords[i + offset] = leftCoordsX.get(idx2); + yCoords[i + offset] = leftCoordsY.get(idx2); + } + + Area area2 = new Area(new Polygon(xCoords, yCoords, xCoords.length)); + area.add(area2); + modified = true; + } + } + } + } + + shapeCache.put(key, area); + return area; + } + + private static void setBitSet(BitSet bitSet, int x1, int x2, int y, int width) { + int idx = width * y + x1; + int idx2 = width * y + x2; + for (; idx < idx2; idx++) { + bitSet.set(idx); + } + } + + private static int findFirst(int[] imgData, int x1, int x2, int y, int width) { + int idx = width * y + x1; + if ((imgData[idx] >>> 24) > 0) { + while (x1 > 0 && (imgData[idx - 1] >>> 24) > 0) { + x1--; + idx--; + } + return x1; + } + + int idx2 = width * y + x2; + for (; idx < idx2; idx++) { + if ((imgData[idx] >>> 24) > 0) { + return x1; + } + + x1++; + } + + return -1; + } + + private static int findRight(int[] imgData, int x, int y, int width) { + int result = x; + int idx = width * y + x; + while (result < width && (imgData[idx] >>> 24) > 0) { + result++; + idx++; + } + + return result; + } + + public static void clearShapeCache() { + shapeCache.clear(); + } + + public static String byteArrayToBase64String(byte[] data) { + return DatatypeConverter.printBase64Binary(data); + } + + /** + * Formats double value (removes .0 from end) + * + * @param d + * @return String + */ + public static String doubleStr(double d) { + String ret = Double.toString(d); + if (ret.endsWith(".0")) { + ret = ret.substring(0, ret.length() - 2); + } + return ret; + } + + public static String byteCountStr(long bytes, boolean si) { + int unit = si ? 1000 : 1024; + if (bytes < unit) { + return bytes + " B"; + } + int exp = (int) (Math.log(bytes) / Math.log(unit)); + String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i"); + return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index 385c7c438..520283e99 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -1,1541 +1,1541 @@ -/* - * Copyright (C) 2010-2015 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui; - -import com.jpexs.decompiler.flash.ApplicationInfo; -import com.jpexs.decompiler.flash.EventListener; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFBundle; -import com.jpexs.decompiler.flash.SWFSourceInfo; -import com.jpexs.decompiler.flash.SearchMode; -import com.jpexs.decompiler.flash.SwfOpenException; -import com.jpexs.decompiler.flash.Version; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.configuration.SwfSpecificConfiguration; -import com.jpexs.decompiler.flash.console.CommandLineArgumentParser; -import com.jpexs.decompiler.flash.console.ContextMenuTools; -import com.jpexs.decompiler.flash.exporters.modes.ExeExportMode; -import com.jpexs.decompiler.flash.gui.pipes.FirstInstance; -import com.jpexs.decompiler.flash.gui.proxy.ProxyFrame; -import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.treeitems.SWFList; -import com.jpexs.helpers.Cache; -import com.jpexs.helpers.CancellableWorker; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.Path; -import com.jpexs.helpers.ProgressListener; -import com.jpexs.helpers.Stopwatch; -import com.jpexs.helpers.streams.SeekableInputStream; -import com.sun.jna.Platform; -import com.sun.jna.platform.win32.Advapi32Util; -import com.sun.jna.platform.win32.Kernel32; -import com.sun.jna.platform.win32.WinReg; -import java.awt.AWTException; -import java.awt.Frame; -import java.awt.GraphicsEnvironment; -import java.awt.MenuItem; -import java.awt.PopupMenu; -import java.awt.SystemTray; -import java.awt.TrayIcon; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.lang.reflect.Field; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.net.URL; -import java.net.URLConnection; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.Map.Entry; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.logging.ConsoleHandler; -import java.util.logging.FileHandler; -import java.util.logging.Formatter; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.logging.SimpleFormatter; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JOptionPane; -import javax.swing.SwingWorker; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; -import javax.swing.filechooser.FileFilter; -import org.pushingpixels.substance.api.SubstanceLookAndFeel; - -/** - * Main executable class - * - * @author JPEXS - */ -public class Main { - - protected static ProxyFrame proxyFrame; - - private static List sourceInfos = new ArrayList<>(); - - public static LoadingDialog loadingDialog; - - private static boolean working = false; - - private static TrayIcon trayIcon; - - private static MenuItem stopMenuItem; - - private static MainFrame mainFrame; - - public static final int UPDATE_SYSTEM_MAJOR = 1; - - public static final int UPDATE_SYSTEM_MINOR = 3; - - private static LoadFromMemoryFrame loadFromMemoryFrame; - - private static LoadFromCacheFrame loadFromCacheFrame; - - private static final Logger logger = Logger.getLogger(Main.class.getName()); - - public static DebugLogDialog debugDialog; - - public static boolean shouldCloseWhenClosingLoadingDialog; - - public static void ensureMainFrame() { - if (mainFrame == null) { - synchronized (Main.class) { - if (mainFrame == null) { - MainFrame frame; - if (Configuration.useRibbonInterface.get()) { - frame = new MainFrameRibbon(); - } else { - frame = new MainFrameClassic(); - } - frame.getPanel().setErrorState(ErrorLogFrame.getInstance().getErrorState()); - mainFrame = frame; - } - } - } - } - - public static MainFrame getMainFrame() { - return mainFrame; - } - - public static void loadFromCache() { - if (loadFromCacheFrame == null) { - loadFromCacheFrame = new LoadFromCacheFrame(); - } - loadFromCacheFrame.setVisible(true); - } - - public static void loadFromMemory() { - if (loadFromMemoryFrame == null) { - loadFromMemoryFrame = new LoadFromMemoryFrame(mainFrame); - } - loadFromMemoryFrame.setVisible(true); - } - - public static void setSubLimiter(boolean value) { - if (value) { - AVM2Code.toSourceLimit = Configuration.sublimiter.get(); - } else { - AVM2Code.toSourceLimit = -1; - } - } - - public static boolean isWorking() { - return working; - } - - public static void startProxy(int port) { - if (proxyFrame == null) { - proxyFrame = new ProxyFrame(mainFrame); - } - - proxyFrame.setPort(port); - addTrayIcon(); - switchProxy(); - } - - public static void showProxy() { - if (proxyFrame == null) { - proxyFrame = new ProxyFrame(mainFrame); - } - proxyFrame.setVisible(true); - proxyFrame.setState(Frame.NORMAL); - } - - public static void startWork(String name) { - startWork(name, -1, null); - } - - public static void startWork(String name, int percent) { - startWork(name, percent, null); - } - - public static void startWork(String name, CancellableWorker worker) { - startWork(name, -1, worker); - } - - public static void startWork(final String name, final int percent, final CancellableWorker worker) { - working = true; - View.execInEventDispatchLater(() -> { - if (mainFrame != null) { - mainFrame.getPanel().setWorkStatus(name, worker); - if (percent == -1) { - mainFrame.getPanel().hidePercent(); - } else { - mainFrame.getPanel().setPercent(percent); - } - } - if (loadingDialog != null) { - loadingDialog.setDetail(name); - loadingDialog.setPercent(percent); - } - if (CommandLineArgumentParser.isCommandLineMode()) { - System.out.println(name); - } - }); - } - - public static void stopWork() { - working = false; - View.execInEventDispatchLater(() -> { - if (mainFrame != null) { - mainFrame.getPanel().setWorkStatus("", null); - } - if (loadingDialog != null) { - loadingDialog.setDetail(""); - } - }); - } - - public static SWFList parseSWF(SWFSourceInfo sourceInfo) throws Exception { - SWFList result = new SWFList(); - - InputStream inputStream = sourceInfo.getInputStream(); - SWFBundle bundle = null; - FileInputStream fis = null; - if (inputStream == null) { - inputStream = new BufferedInputStream(fis = new FileInputStream(sourceInfo.getFile())); - bundle = sourceInfo.getBundle(false, SearchMode.ALL); - logger.log(Level.INFO, "Load file: {0}", sourceInfo.getFile()); - } else if (inputStream instanceof SeekableInputStream - || inputStream instanceof BufferedInputStream) { - try { - inputStream.reset(); - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - logger.log(Level.INFO, "Load stream: {0}", sourceInfo.getFileTitle()); - } - - Stopwatch sw = Stopwatch.startNew(); - if (bundle != null) { - result.bundle = bundle; - result.name = new File(sourceInfo.getFileTitleOrName()).getName(); - for (Entry streamEntry : bundle.getAll().entrySet()) { - InputStream stream = streamEntry.getValue(); - stream.reset(); - CancellableWorker worker = new CancellableWorker() { - @Override - public SWF doInBackground() throws Exception { - SWF swf = new SWF(stream, null, streamEntry.getKey(), new ProgressListener() { - @Override - public void progress(int p) { - startWork(AppStrings.translate("work.reading.swf"), p); - } - }, Configuration.parallelSpeedUp.get()); - return swf; - } - }; - loadingDialog.setWroker(worker); - worker.execute(); - - try { - result.add(worker.get()); - } catch (CancellationException ex) { - logger.log(Level.WARNING, "Loading SWF {0} was cancelled.", streamEntry.getKey()); - } - } - } else { - InputStream fInputStream = inputStream; - CancellableWorker worker = new CancellableWorker() { - @Override - public SWF doInBackground() throws Exception { - SWF swf = new SWF(fInputStream, sourceInfo.getFile(), sourceInfo.getFileTitle(), new ProgressListener() { - @Override - public void progress(int p) { - startWork(AppStrings.translate("work.reading.swf"), p); - } - }, Configuration.parallelSpeedUp.get()); - return swf; - } - }; - if (loadingDialog != null) { - loadingDialog.setWroker(worker); - } - worker.execute(); - - try { - result.add(worker.get()); - } catch (CancellationException ex) { - logger.log(Level.WARNING, "Loading SWF {0} was cancelled.", sourceInfo.getFileTitleOrName()); - } - } - - if (fis != null) { - logger.log(Level.INFO, "File loaded in {0} seconds.", (sw.getElapsedMilliseconds() / 1000)); - fis.close(); - } else { - logger.log(Level.INFO, "Stream loaded in {0} seconds.", (sw.getElapsedMilliseconds() / 1000)); - } - - result.sourceInfo = sourceInfo; - for (SWF swf : result) { - logger.log(Level.INFO, ""); - logger.log(Level.INFO, "== File information =="); - logger.log(Level.INFO, "Size: {0}", Helper.formatFileSize(swf.fileSize)); - logger.log(Level.INFO, "Flash version: {0}", swf.version); - int width = (int) ((swf.displayRect.Xmax - swf.displayRect.Xmin) / SWF.unitDivisor); - int height = (int) ((swf.displayRect.Ymax - swf.displayRect.Ymin) / SWF.unitDivisor); - logger.log(Level.INFO, "Width: {0}", width); - logger.log(Level.INFO, "Height: {0}", height); - - swf.swfList = result; - swf.addEventListener(new EventListener() { - @Override - public void handleExportingEvent(String type, int index, int count, Object data) { - String text = AppStrings.translate("work.exporting"); - if (type != null && type.length() > 0) { - text += " " + type; - } - - startWork(text + " " + index + "/" + count + " " + data); - } - - @Override - public void handleExportedEvent(String type, int index, int count, Object data) { - String text = AppStrings.translate("work.exported"); - if (type != null && type.length() > 0) { - text += " " + type; - } - - startWork(text + " " + index + "/" + count + " " + data); - } - - @Override - public void handleEvent(String event, Object data) { - if (event.equals("exporting") || event.equals("exported")) { - throw new Error("Event is not supported by this handler."); - } - if (event.equals("getVariables")) { - startWork(AppStrings.translate("work.gettingvariables") + "..." + (String) data); - } - if (event.equals("deobfuscate")) { - startWork(AppStrings.translate("work.deobfuscating") + "..." + (String) data); - } - if (event.equals("rename")) { - startWork(AppStrings.translate("work.renaming") + "..." + (String) data); - } - } - }); - } - - return result; - } - - public static void saveFile(SWF swf, String outfile) throws IOException { - saveFile(swf, outfile, SaveFileMode.SAVE, null); - } - - public static void saveFile(SWF swf, String outfile, SaveFileMode mode, ExeExportMode exeExportMode) throws IOException { - if (mode == SaveFileMode.SAVEAS && !swf.swfList.isBundle()) { - swf.setFile(outfile); - swf.swfList.sourceInfo.setFile(outfile); - } - File outfileF = new File(outfile); - File tmpFile = new File(outfile + ".tmp"); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tmpFile))) { - if (mode == SaveFileMode.EXE) { - switch (exeExportMode) { - case WRAPPER: - InputStream exeStream = View.class.getClassLoader().getResourceAsStream("com/jpexs/helpers/resource/Swf2Exe.bin"); - byte[] buffer = new byte[4096]; - int bytesRead; - while ((bytesRead = exeStream.read(buffer)) != -1) { - fos.write(buffer, 0, bytesRead); - } - int width = swf.displayRect.Xmax - swf.displayRect.Xmin; - int height = swf.displayRect.Ymax - swf.displayRect.Ymin; - fos.write(width & 0xff); - fos.write((width >> 8) & 0xff); - fos.write((width >> 16) & 0xff); - fos.write((width >> 24) & 0xff); - fos.write(height & 0xff); - fos.write((height >> 8) & 0xff); - fos.write((height >> 16) & 0xff); - fos.write((height >> 24) & 0xff); - fos.write(Configuration.saveAsExeScaleMode.get()); - break; - case PROJECTOR_WIN: - // todo - break; - case PROJECTOR_MAC: - // todo - break; - } - } - swf.saveTo(fos); - } - if (tmpFile.exists()) { - if (tmpFile.length() > 0) { - outfileF.delete(); - if (!tmpFile.renameTo(outfileF)) { - tmpFile.delete(); - throw new IOException("Cannot access " + outfile); - } - } else { - throw new IOException("Output is empty"); - } - } - } - - private static class OpenFileWorker extends SwingWorker { - - private final SWFSourceInfo[] sourceInfos; - - private final Runnable executeAfterOpen; - - public OpenFileWorker(SWFSourceInfo sourceInfo) { - this(sourceInfo, null); - } - - public OpenFileWorker(SWFSourceInfo sourceInfo, Runnable executeAfterOpen) { - this.sourceInfos = new SWFSourceInfo[]{sourceInfo}; - this.executeAfterOpen = executeAfterOpen; - } - - public OpenFileWorker(SWFSourceInfo[] sourceInfos) { - this(sourceInfos, null); - } - - public OpenFileWorker(SWFSourceInfo[] sourceInfos, Runnable executeAfterOpen) { - this.sourceInfos = sourceInfos; - this.executeAfterOpen = executeAfterOpen; - } - - @Override - protected Object doInBackground() throws Exception { - boolean first = true; - SWF firstSWF = null; - for (final SWFSourceInfo sourceInfo : sourceInfos) { - SWFList swfs = null; - try { - Main.startWork(AppStrings.translate("work.reading.swf") + "..."); - try { - swfs = parseSWF(sourceInfo); - } catch (ExecutionException ex) { - Throwable cause = ex.getCause(); - if (cause instanceof SwfOpenException) { - throw (SwfOpenException) cause; - } - - throw ex; - } - } catch (OutOfMemoryError ex) { - logger.log(Level.SEVERE, null, ex); - View.showMessageDialog(null, "Cannot load SWF file. Out of memory."); - continue; - } catch (SwfOpenException ex) { - logger.log(Level.SEVERE, null, ex); - View.showMessageDialog(null, ex.getMessage()); - continue; - } catch (Exception ex) { - logger.log(Level.SEVERE, null, ex); - View.showMessageDialog(null, "Cannot load SWF file."); - continue; - } - - final SWFList swfs1 = swfs; - final boolean first1 = first; - first = false; - if (firstSWF == null) { - firstSWF = swfs1.get(0); - } - - try { - View.execInEventDispatch(() -> { - Main.startWork(AppStrings.translate("work.creatingwindow") + "..."); - ensureMainFrame(); - mainFrame.getPanel().load(swfs1, first1); - }); - } catch (Exception ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - loadingDialog.setVisible(false); - shouldCloseWhenClosingLoadingDialog = false; - - final SWF fswf = firstSWF; - View.execInEventDispatch(() -> { - if (mainFrame != null) { - mainFrame.setVisible(true); - } - - Main.stopWork(); - - if (mainFrame != null && Configuration.gotoMainClassOnStartup.get()) { - mainFrame.getPanel().gotoDocumentClass(fswf); - } - - if (mainFrame != null && fswf != null) { - SwfSpecificConfiguration swfConf = Configuration.getSwfSpecificConfiguration(fswf.getShortFileName()); - if (swfConf != null) { - String pathStr = swfConf.lastSelectedPath; - mainFrame.getPanel().tagTree.setSelectionPathString(pathStr); - } - } - - if (executeAfterOpen != null) { - executeAfterOpen.run(); - } - }); - - return true; - } - } - - public static boolean reloadSWFs() { - CancellableWorker.cancelBackgroundThreads(); - if (Main.sourceInfos.isEmpty()) { - Helper.freeMem(); - showModeFrame(); - return true; - } else { - SWFSourceInfo[] sourceInfosCopy = new SWFSourceInfo[sourceInfos.size()]; - sourceInfos.toArray(sourceInfosCopy); - sourceInfos.clear(); - openFile(sourceInfosCopy); - return true; - } - } - - public static void reloadApp() { - if (debugDialog != null) { - debugDialog.setVisible(false); - debugDialog.dispose(); - debugDialog = null; - } - if (loadingDialog != null) { - synchronized (Main.class) { - if (loadingDialog != null) { - loadingDialog.setVisible(false); - loadingDialog.dispose(); - loadingDialog = null; - } - } - } - if (proxyFrame != null) { - proxyFrame.setVisible(false); - proxyFrame.dispose(); - proxyFrame = null; - } - if (loadFromMemoryFrame != null) { - loadFromMemoryFrame.setVisible(false); - loadFromMemoryFrame.dispose(); - loadFromMemoryFrame = null; - } - if (loadFromCacheFrame != null) { - loadFromCacheFrame.setVisible(false); - loadFromCacheFrame.dispose(); - loadFromCacheFrame = null; - } - if (mainFrame != null) { - mainFrame.setVisible(false); - mainFrame.getPanel().closeAll(); - mainFrame.dispose(); - mainFrame = null; - } - FontTag.reload(); - Cache.clearAll(); - initGui(); - reloadSWFs(); - } - - public static OpenFileResult openFile(String swfFile, String fileTitle) { - return openFile(swfFile, fileTitle, null); - } - - public static OpenFileResult openFile(String swfFile, String fileTitle, Runnable executeAfterOpen) { - try { - File file = new File(swfFile); - if (!file.exists()) { - View.showMessageDialog(null, AppStrings.translate("open.error.fileNotFound"), AppStrings.translate("open.error"), JOptionPane.ERROR_MESSAGE); - return OpenFileResult.NOT_FOUND; - } - swfFile = file.getCanonicalPath(); - Configuration.addRecentFile(swfFile); - SWFSourceInfo sourceInfo = new SWFSourceInfo(null, swfFile, fileTitle); - OpenFileResult openResult = openFile(sourceInfo); - return openResult; - } catch (IOException ex) { - View.showMessageDialog(null, AppStrings.translate("open.error.cannotOpen"), AppStrings.translate("open.error"), JOptionPane.ERROR_MESSAGE); - return OpenFileResult.ERROR; - } - } - - public static OpenFileResult openFile(SWFSourceInfo sourceInfo) { - return openFile(new SWFSourceInfo[]{sourceInfo}); - } - - public static OpenFileResult openFile(SWFSourceInfo sourceInfo, Runnable executeAfterOpen) { - return openFile(new SWFSourceInfo[]{sourceInfo}, executeAfterOpen); - } - - public static OpenFileResult openFile(SWFSourceInfo[] newSourceInfos) { - return openFile(newSourceInfos, null); - } - - public static OpenFileResult openFile(SWFSourceInfo[] newSourceInfos, Runnable executeAfterOpen) { - if (mainFrame != null && !Configuration.openMultipleFiles.get()) { - sourceInfos.clear(); - mainFrame.getPanel().closeAll(); - mainFrame.setVisible(false); - Helper.freeMem(); - } - - loadingDialog.setVisible(true); - OpenFileWorker wrk = new OpenFileWorker(newSourceInfos, executeAfterOpen); - wrk.execute(); - sourceInfos.addAll(Arrays.asList(newSourceInfos)); - return OpenFileResult.OK; - } - - public static void closeFile(SWFList swf) { - sourceInfos.remove(swf.sourceInfo); - mainFrame.getPanel().close(swf); - } - - public static void closeAll() { - sourceInfos.clear(); - mainFrame.getPanel().closeAll(); - } - - public static boolean saveFileDialog(SWF swf, final SaveFileMode mode) { - JFileChooser fc = new JFileChooser(); - fc.setCurrentDirectory(new File(Configuration.lastSaveDir.get())); - String ext = ".swf"; - switch (mode) { - case SAVE: - case SAVEAS: - if (swf.getFile() != null) { - ext = Path.getExtension(swf.getFile()); - } - break; - case EXE: - ext = ".exe"; - break; - } - - FileFilter swfFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".swf")) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.swf"); - } - }; - - FileFilter gfxFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".gfx")) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.gfx"); - } - }; - - ExeExportMode exeExportMode = null; - if (mode == SaveFileMode.EXE) { - exeExportMode = Configuration.exeExportMode.get(); - String filterDescription = null; - switch (exeExportMode) { - case WRAPPER: - ext = ".exe"; - filterDescription = "filter.exe"; - break; - case PROJECTOR_WIN: - ext = ".exe"; - filterDescription = "filter.exe"; - break; - case PROJECTOR_MAC: - ext = ".dmg"; - filterDescription = "filter.dmg"; - break; - /*case PROJECTOR_LINUX: - // linux projector is compressed with tar.gz - // todo: decompress - ext = ""; - filterDescription = "filter.linuxExe"; - break;*/ - } - - String fext = ext; - String ffilterDescription = filterDescription; - FileFilter exeFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(fext)) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate(ffilterDescription); - } - }; - fc.setFileFilter(exeFilter); - } else { - if (swf.gfx) { - fc.addChoosableFileFilter(swfFilter); - fc.setFileFilter(gfxFilter); - } else { - fc.setFileFilter(swfFilter); - fc.addChoosableFileFilter(gfxFilter); - } - } - final String extension = ext; - fc.setAcceptAllFileFilterUsed(false); - JFrame f = new JFrame(); - View.setWindowIcon(f); - if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) { - File file = Helper.fixDialogFile(fc.getSelectedFile()); - FileFilter selFilter = fc.getFileFilter(); - try { - String fileName = file.getAbsolutePath(); - if (selFilter == swfFilter) { - if (!fileName.toLowerCase().endsWith(extension)) { - fileName += extension; - } - swf.gfx = false; - } - if (selFilter == gfxFilter) { - if (!fileName.toLowerCase().endsWith(".gfx")) { - fileName += ".gfx"; - } - swf.gfx = true; - } - Main.saveFile(swf, fileName, mode, exeExportMode); - Configuration.lastSaveDir.set(file.getParentFile().getAbsolutePath()); - return true; - } catch (IOException ex) { - View.showMessageDialog(null, AppStrings.translate("error.file.write")); - } - } - return false; - } - - public static boolean openFileDialog() { - JFileChooser fc = new JFileChooser(); - if (Configuration.openMultipleFiles.get()) { - fc.setMultiSelectionEnabled(true); - } - fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); - FileFilter allSupportedFilter = new FileFilter() { - private final String[] supportedExtensions = new String[]{".swf", ".gfx", ".swc", ".zip"}; - - @Override - public boolean accept(File f) { - String name = f.getName().toLowerCase(); - for (String ext : supportedExtensions) { - if (name.endsWith(ext)) { - return true; - } - } - return f.isDirectory(); - } - - @Override - public String getDescription() { - String exts = Helper.joinStrings(supportedExtensions, "*%s", "; "); - return AppStrings.translate("filter.supported") + " (" + exts + ")"; - } - }; - fc.setFileFilter(allSupportedFilter); - FileFilter swfFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".swf")) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.swf"); - } - }; - fc.addChoosableFileFilter(swfFilter); - - FileFilter swcFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".swc")) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.swc"); - } - }; - fc.addChoosableFileFilter(swcFilter); - - FileFilter gfxFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".gfx")) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.gfx"); - } - }; - fc.addChoosableFileFilter(gfxFilter); - - FileFilter zipFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".zip")) || (f.isDirectory()); - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.zip"); - } - }; - fc.addChoosableFileFilter(zipFilter); - - FileFilter binaryFilter = new FileFilter() { - @Override - public boolean accept(File f) { - return true; - } - - @Override - public String getDescription() { - return AppStrings.translate("filter.binary"); - } - }; - fc.addChoosableFileFilter(binaryFilter); - - fc.setAcceptAllFileFilterUsed(false); - JFrame f = new JFrame(); - View.setWindowIcon(f); - int returnVal = fc.showOpenDialog(f); - if (returnVal == JFileChooser.APPROVE_OPTION) { - Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); - File[] selFiles = fc.getSelectedFiles(); - for (File file : selFiles) { - File selfile = Helper.fixDialogFile(file); - Main.openFile(selfile.getAbsolutePath(), null); - } - return true; - } else { - return false; - } - } - - public static void displayErrorFrame() { - ErrorLogFrame.getInstance().setVisible(true); - } - - private static void initGui() { - if (GraphicsEnvironment.isHeadless()) { - System.err.println("Error: Your system does not support Graphic User Interface"); - exit(); - } - if (Configuration.useRibbonInterface.get()) { - View.setLookAndFeel(); - } else { - try { - UIManager.put(SubstanceLookAndFeel.COLORIZATION_FACTOR, null); - UIManager.put("Tree.expandedIcon", null); - UIManager.put("Tree.collapsedIcon", null); - UIManager.put("ColorChooserUI", null); - UIManager.put("ColorChooser.swatchesRecentSwatchSize", null); - UIManager.put("ColorChooser.swatchesSwatchSize", null); - UIManager.put("RibbonApplicationMenuPopupPanelUI", null); - UIManager.put("RibbonApplicationMenuButtonUI", null); - UIManager.put("ProgressBarUI", null); - UIManager.put("TextField.background", null); - UIManager.put("FormattedTextField.background", null); - UIManager.put("CommandButtonUI", null); - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - ErrorLogFrame.createNewInstance(); - - autoCheckForUpdates(); - offerAssociation(); - loadingDialog = new LoadingDialog(); - } - - public static void showModeFrame() { - ensureMainFrame(); - mainFrame.setVisible(true); - } - - private static void offerAssociation() { - boolean offered = Configuration.offeredAssociation.get(); - if (!offered) { - if (Platform.isWindows()) { - if ((!ContextMenuTools.isAddedToContextMenu()) && View.showConfirmDialog(null, "Do you want to add FFDec to context menu of SWF files?\n(Can be changed later from main menu)", "Context menu", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { - ContextMenuTools.addToContextMenu(true, false); - } - } - - Configuration.offeredAssociation.set(true); - } - } - - public static void initLang() { - if (GraphicsEnvironment.isHeadless()) { //No GUI in OS - return; - } - if (!Configuration.locale.hasValue()) { - if (Platform.isWindows()) { - //Load from Installer - String uninstKey = "{E618D276-6596-41F4-8A98-447D442A77DB}_is1"; - uninstKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + uninstKey; - try { - if (Advapi32Util.registryKeyExists(WinReg.HKEY_LOCAL_MACHINE, uninstKey)) { - if (Advapi32Util.registryValueExists(WinReg.HKEY_LOCAL_MACHINE, uninstKey, "NSIS: Language")) { - String installedLoc = Advapi32Util.registryGetStringValue(WinReg.HKEY_LOCAL_MACHINE, uninstKey, "NSIS: Language"); - int lcid = Integer.parseInt(installedLoc); - char buf[] = new char[9]; - int cnt = Kernel32.INSTANCE.GetLocaleInfo(lcid, Kernel32.LOCALE_SISO639LANGNAME, buf, 9); - String langCode = new String(buf, 0, cnt).trim().toLowerCase(); - - cnt = Kernel32.INSTANCE.GetLocaleInfo(lcid, Kernel32.LOCALE_SISO3166CTRYNAME, buf, 9); - String countryCode = new String(buf, 0, cnt).trim().toLowerCase(); - - List langs = Arrays.asList(SelectLanguageDialog.getAvailableLanguages()); - for (int i = 0; i < langs.size(); i++) { - langs.set(i, langs.get(i).toLowerCase()); - } - - String selectedLang = null; - - if (langs.contains(langCode + "-" + countryCode)) { - selectedLang = SelectLanguageDialog.getAvailableLanguages()[langs.indexOf(langCode + "-" + countryCode)]; - } else if (langs.contains(langCode)) { - selectedLang = SelectLanguageDialog.getAvailableLanguages()[langs.indexOf(langCode)]; - } - if (selectedLang != null) { - Configuration.locale.set(selectedLang); - } - } - } - } catch (Exception ex) { - //ignore - } - } - } - Locale.setDefault(Locale.forLanguageTag(Configuration.locale.get())); - AppStrings.updateLanguage(); - - try { - Class cl = Class.forName("org.pushingpixels.substance.api.SubstanceLookAndFeel"); - Field field = cl.getDeclaredField("LABEL_BUNDLE"); - field.setAccessible(true); - field.set(null, null); - } catch (Throwable ex) { - logger.log(Level.SEVERE, null, ex); - } - - UIManager.put("OptionPane.okButtonText", AppStrings.translate("button.ok")); - UIManager.put("OptionPane.yesButtonText", AppStrings.translate("button.yes")); - UIManager.put("OptionPane.noButtonText", AppStrings.translate("button.no")); - UIManager.put("OptionPane.cancelButtonText", AppStrings.translate("button.cancel")); - UIManager.put("OptionPane.messageDialogTitle", AppStrings.translate("dialog.message.title")); - UIManager.put("OptionPane.titleText", AppStrings.translate("dialog.select.title")); - - UIManager.put("FileChooser.acceptAllFileFilterText", AppStrings.translate("FileChooser.acceptAllFileFilterText")); - UIManager.put("FileChooser.lookInLabelText", AppStrings.translate("FileChooser.lookInLabelText")); - UIManager.put("FileChooser.cancelButtonText", AppStrings.translate("button.cancel")); - UIManager.put("FileChooser.cancelButtonToolTipText", AppStrings.translate("button.cancel")); - UIManager.put("FileChooser.openButtonText", AppStrings.translate("FileChooser.openButtonText")); - UIManager.put("FileChooser.openButtonToolTipText", AppStrings.translate("FileChooser.openButtonToolTipText")); - UIManager.put("FileChooser.filesOfTypeLabelText", AppStrings.translate("FileChooser.filesOfTypeLabelText")); - UIManager.put("FileChooser.fileNameLabelText", AppStrings.translate("FileChooser.fileNameLabelText")); - UIManager.put("FileChooser.listViewButtonToolTipText", AppStrings.translate("FileChooser.listViewButtonToolTipText")); - UIManager.put("FileChooser.listViewButtonAccessibleName", AppStrings.translate("FileChooser.listViewButtonAccessibleName")); - UIManager.put("FileChooser.detailsViewButtonToolTipText", AppStrings.translate("FileChooser.detailsViewButtonToolTipText")); - UIManager.put("FileChooser.detailsViewButtonAccessibleName", AppStrings.translate("FileChooser.detailsViewButtonAccessibleName")); - UIManager.put("FileChooser.upFolderToolTipText", AppStrings.translate("FileChooser.upFolderToolTipText")); - UIManager.put("FileChooser.upFolderAccessibleName", AppStrings.translate("FileChooser.upFolderAccessibleName")); - UIManager.put("FileChooser.homeFolderToolTipText", AppStrings.translate("FileChooser.homeFolderToolTipText")); - UIManager.put("FileChooser.homeFolderAccessibleName", AppStrings.translate("FileChooser.homeFolderAccessibleName")); - UIManager.put("FileChooser.fileNameHeaderText", AppStrings.translate("FileChooser.fileNameHeaderText")); - UIManager.put("FileChooser.fileSizeHeaderText", AppStrings.translate("FileChooser.fileSizeHeaderText")); - UIManager.put("FileChooser.fileTypeHeaderText", AppStrings.translate("FileChooser.fileTypeHeaderText")); - UIManager.put("FileChooser.fileDateHeaderText", AppStrings.translate("FileChooser.fileDateHeaderText")); - UIManager.put("FileChooser.fileAttrHeaderText", AppStrings.translate("FileChooser.fileAttrHeaderText")); - UIManager.put("FileChooser.openDialogTitleText", AppStrings.translate("FileChooser.openDialogTitleText")); - UIManager.put("FileChooser.directoryDescriptionText", AppStrings.translate("FileChooser.directoryDescriptionText")); - UIManager.put("FileChooser.directoryOpenButtonText", AppStrings.translate("FileChooser.directoryOpenButtonText")); - UIManager.put("FileChooser.directoryOpenButtonToolTipText", AppStrings.translate("FileChooser.directoryOpenButtonToolTipText")); - UIManager.put("FileChooser.fileDescriptionText", AppStrings.translate("FileChooser.fileDescriptionText")); - UIManager.put("FileChooser.fileNameLabelText", AppStrings.translate("FileChooser.fileNameLabelText")); - UIManager.put("FileChooser.helpButtonText", AppStrings.translate("FileChooser.helpButtonText")); - UIManager.put("FileChooser.helpButtonToolTipText", AppStrings.translate("FileChooser.helpButtonToolTipText")); - UIManager.put("FileChooser.newFolderAccessibleName", AppStrings.translate("FileChooser.newFolderAccessibleName")); - UIManager.put("FileChooser.newFolderErrorText", AppStrings.translate("FileChooser.newFolderErrorText")); - UIManager.put("FileChooser.newFolderToolTipText", AppStrings.translate("FileChooser.newFolderToolTipText")); - UIManager.put("FileChooser.other.newFolder", AppStrings.translate("FileChooser.other.newFolder")); - UIManager.put("FileChooser.other.newFolder.subsequent", AppStrings.translate("FileChooser.other.newFolder.subsequent")); - UIManager.put("FileChooser.win32.newFolder", AppStrings.translate("FileChooser.win32.newFolder")); - UIManager.put("FileChooser.win32.newFolder.subsequent", AppStrings.translate("FileChooser.win32.newFolder.subsequent")); - UIManager.put("FileChooser.saveButtonText", AppStrings.translate("FileChooser.saveButtonText")); - UIManager.put("FileChooser.saveButtonToolTipText", AppStrings.translate("FileChooser.saveButtonToolTipText")); - UIManager.put("FileChooser.saveDialogTitleText", AppStrings.translate("FileChooser.saveDialogTitleText")); - UIManager.put("FileChooser.saveInLabelText", AppStrings.translate("FileChooser.saveInLabelText")); - UIManager.put("FileChooser.updateButtonText", AppStrings.translate("FileChooser.updateButtonText")); - UIManager.put("FileChooser.updateButtonToolTipText", AppStrings.translate("FileChooser.updateButtonToolTipText")); - - UIManager.put("FileChooser.detailsViewActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.detailsViewActionLabel.textAndMnemonic")); - UIManager.put("FileChooser.detailsViewButtonToolTip.textAndMnemonic", AppStrings.translate("FileChooser.detailsViewButtonToolTip.textAndMnemonic")); - UIManager.put("FileChooser.fileAttrHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileAttrHeader.textAndMnemonic")); - UIManager.put("FileChooser.fileDateHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileDateHeader.textAndMnemonic")); - UIManager.put("FileChooser.fileNameHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileNameHeader.textAndMnemonic")); - UIManager.put("FileChooser.fileNameLabel.textAndMnemonic", AppStrings.translate("FileChooser.fileNameLabel.textAndMnemonic")); - UIManager.put("FileChooser.fileSizeHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileSizeHeader.textAndMnemonic")); - UIManager.put("FileChooser.fileTypeHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileTypeHeader.textAndMnemonic")); - UIManager.put("FileChooser.filesOfTypeLabel.textAndMnemonic", AppStrings.translate("FileChooser.filesOfTypeLabel.textAndMnemonic")); - UIManager.put("FileChooser.folderNameLabel.textAndMnemonic", AppStrings.translate("FileChooser.folderNameLabel.textAndMnemonic")); - UIManager.put("FileChooser.homeFolderToolTip.textAndMnemonic", AppStrings.translate("FileChooser.homeFolderToolTip.textAndMnemonic")); - UIManager.put("FileChooser.listViewActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.listViewActionLabel.textAndMnemonic")); - UIManager.put("FileChooser.listViewButtonToolTip.textAndMnemonic", AppStrings.translate("FileChooser.listViewButtonToolTip.textAndMnemonic")); - UIManager.put("FileChooser.lookInLabel.textAndMnemonic", AppStrings.translate("FileChooser.lookInLabel.textAndMnemonic")); - UIManager.put("FileChooser.newFolderActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.newFolderActionLabel.textAndMnemonic")); - UIManager.put("FileChooser.newFolderToolTip.textAndMnemonic", AppStrings.translate("FileChooser.newFolderToolTip.textAndMnemonic")); - UIManager.put("FileChooser.refreshActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.refreshActionLabel.textAndMnemonic")); - UIManager.put("FileChooser.saveInLabel.textAndMnemonic", AppStrings.translate("FileChooser.saveInLabel.textAndMnemonic")); - UIManager.put("FileChooser.upFolderToolTip.textAndMnemonic", AppStrings.translate("FileChooser.upFolderToolTip.textAndMnemonic")); - UIManager.put("FileChooser.viewMenuButtonAccessibleName", AppStrings.translate("FileChooser.viewMenuButtonAccessibleName")); - UIManager.put("FileChooser.viewMenuButtonToolTipText", AppStrings.translate("FileChooser.viewMenuButtonToolTipText")); - UIManager.put("FileChooser.viewMenuLabel.textAndMnemonic", AppStrings.translate("FileChooser.viewMenuLabel.textAndMnemonic")); - UIManager.put("FileChooser.newFolderActionLabelText", AppStrings.translate("FileChooser.newFolderActionLabelText")); - UIManager.put("FileChooser.listViewActionLabelText", AppStrings.translate("FileChooser.listViewActionLabelText")); - UIManager.put("FileChooser.detailsViewActionLabelText", AppStrings.translate("FileChooser.detailsViewActionLabelText")); - UIManager.put("FileChooser.refreshActionLabelText", AppStrings.translate("FileChooser.refreshActionLabelText")); - UIManager.put("FileChooser.sortMenuLabelText", AppStrings.translate("FileChooser.sortMenuLabelText")); - UIManager.put("FileChooser.viewMenuLabelText", AppStrings.translate("FileChooser.viewMenuLabelText")); - UIManager.put("FileChooser.fileSizeKiloBytes", AppStrings.translate("FileChooser.fileSizeKiloBytes")); - UIManager.put("FileChooser.fileSizeMegaBytes", AppStrings.translate("FileChooser.fileSizeMegaBytes")); - UIManager.put("FileChooser.fileSizeGigaBytes", AppStrings.translate("FileChooser.fileSizeGigaBytes")); - UIManager.put("FileChooser.folderNameLabelText", AppStrings.translate("FileChooser.folderNameLabelText")); - - UIManager.put("ColorChooser.okText", AppStrings.translate("ColorChooser.okText")); - UIManager.put("ColorChooser.cancelText", AppStrings.translate("ColorChooser.cancelText")); - UIManager.put("ColorChooser.resetText", AppStrings.translate("ColorChooser.resetText")); - UIManager.put("ColorChooser.previewText", AppStrings.translate("ColorChooser.previewText")); - UIManager.put("ColorChooser.swatchesNameText", AppStrings.translate("ColorChooser.swatchesNameText")); - UIManager.put("ColorChooser.swatchesRecentText", AppStrings.translate("ColorChooser.swatchesRecentText")); - UIManager.put("ColorChooser.sampleText", AppStrings.translate("ColorChooser.sampleText")); - - Helper.decompilationErrorAdd = AppStrings.translate(Configuration.autoDeobfuscate.get() ? "deobfuscation.comment.failed" : "deobfuscation.comment.tryenable"); - } - - /** - * Clear old FFDec/JavactiveX temp files - */ - private static void clearTemp() { - String tempDirPath = System.getProperty("java.io.tmpdir"); - if (tempDirPath == null) { - return; - } - File tempDir = new File(tempDirPath); - if (!tempDir.exists()) { - return; - } - File delFiles[] = tempDir.listFiles(new FilenameFilter() { - - @Override - public boolean accept(File dir, String name) { - return name.matches("ffdec_cache.*\\.tmp") || name.matches("javactivex_.*\\.exe") || name.matches("temp[0-9]+\\.swf") || name.matches("ffdec_view_.*\\.swf"); - } - }); - for (File f : delFiles) { - try { - f.delete(); - } catch (Exception ex) { - //ignore - } - } - } - - /** - * @param args the command line arguments - * @throws IOException On error - */ - public static void main(String[] args) throws IOException { - - clearTemp(); - String pluginPath = Configuration.pluginPath.get(); - if (pluginPath != null && !pluginPath.isEmpty()) { - try { - SWFDecompilerPlugin.loadPlugin(pluginPath); - } catch (Throwable e) { - View.showMessageDialog(null, "Failed to load plugin: " + pluginPath); - } - } - - AppStrings.setResourceClass(MainFrame.class); - initLogging(Configuration.debugMode.get()); - initLang(); - - if (Configuration.cacheOnDisk.get()) { - Cache.setStorageType(Cache.STORAGE_FILES); - } else { - Cache.setStorageType(Cache.STORAGE_MEMORY); - } - - if (args.length == 0) { - View.execInEventDispatch(() -> { - initGui(); - if (Configuration.allowOnlyOneInstance.get() && FirstInstance.focus()) { //Try to focus first instance - Main.exit(); - } else { - showModeFrame(); - reloadLastSession(); - } - }); - } else { - String[] filesToOpen = CommandLineArgumentParser.parseArguments(args); - if (filesToOpen != null && filesToOpen.length > 0) { - View.execInEventDispatch(() -> { - initGui(); - shouldCloseWhenClosingLoadingDialog = true; - if (Configuration.allowOnlyOneInstance.get() && FirstInstance.openFiles(Arrays.asList(filesToOpen))) { //Try to open in first instance - Main.exit(); - } else { - for (String fileToOpen : filesToOpen) { - openFile(fileToOpen, null); - } - } - }); - } - } - } - - private static void reloadLastSession() { - if (Configuration.saveSessionOnExit.get()) { - String lastSession = Configuration.lastSessionFiles.get(); - if (lastSession != null && lastSession.length() > 0) { - String[] filesToOpen = lastSession.split(File.pathSeparator, -1); - List exfiles = new ArrayList<>(); - for (int i = 0; i < filesToOpen.length; i++) { - if (new File(filesToOpen[i]).exists()) { - exfiles.add(filesToOpen[i]); - } - } - SWFSourceInfo[] sourceInfos = new SWFSourceInfo[exfiles.size()]; - for (int i = 0; i < exfiles.size(); i++) { - sourceInfos[i] = new SWFSourceInfo(null, exfiles.get(i), null); - } - if (sourceInfos.length > 0) { - openFile(sourceInfos, () -> { - mainFrame.getPanel().tagTree.setSelectionPathString(Configuration.lastSessionSelection.get()); - }); - } - } - } - } - - public static String tempFile(String url) throws IOException { - File f = new File(Configuration.getFFDecHome() + "saved" + File.separator); - Path.createDirectorySafe(f); - return Configuration.getFFDecHome() + "saved" + File.separator + "asdec_" + Integer.toHexString(url.hashCode()) + ".tmp"; - } - - public static void removeTrayIcon() { - if (SystemTray.isSupported()) { - SystemTray tray = SystemTray.getSystemTray(); - if (trayIcon != null) { - tray.remove(trayIcon); - trayIcon = null; - } - } - } - - public static void switchProxy() { - proxyFrame.switchState(); - if (stopMenuItem != null) { - if (proxyFrame.isRunning()) { - stopMenuItem.setLabel(AppStrings.translate("proxy.stop")); - } else { - stopMenuItem.setLabel(AppStrings.translate("proxy.start")); - } - } - } - - public static void addTrayIcon() { - if (trayIcon != null) { - return; - } - if (SystemTray.isSupported()) { - SystemTray tray = SystemTray.getSystemTray(); - trayIcon = new TrayIcon(View.loadImage("proxy16"), ApplicationInfo.VENDOR + " " + ApplicationInfo.SHORT_APPLICATION_NAME + " " + AppStrings.translate("proxy")); - trayIcon.setImageAutoSize(true); - PopupMenu trayPopup = new PopupMenu(); - - ActionListener trayListener = new ActionListener() { - /** - * Invoked when an action occurs. - */ - @Override - public void actionPerformed(ActionEvent e) { - if (e.getActionCommand().equals("EXIT")) { - Main.exit(); - } - if (e.getActionCommand().equals("SHOW")) { - Main.showProxy(); - } - if (e.getActionCommand().equals("SWITCH")) { - Main.switchProxy(); - } - } - }; - - MenuItem showMenuItem = new MenuItem(AppStrings.translate("proxy.show")); - showMenuItem.setActionCommand("SHOW"); - showMenuItem.addActionListener(trayListener); - trayPopup.add(showMenuItem); - stopMenuItem = new MenuItem(AppStrings.translate("proxy.start")); - stopMenuItem.setActionCommand("SWITCH"); - stopMenuItem.addActionListener(trayListener); - trayPopup.add(stopMenuItem); - trayPopup.addSeparator(); - MenuItem exitMenuItem = new MenuItem(AppStrings.translate("exit")); - exitMenuItem.setActionCommand("EXIT"); - exitMenuItem.addActionListener(trayListener); - trayPopup.add(exitMenuItem); - - trayIcon.setPopupMenu(trayPopup); - trayIcon.addMouseListener(new MouseAdapter() { - /** - * {@inheritDoc} - */ - @Override - public void mouseClicked(MouseEvent e) { - if (e.getButton() == MouseEvent.BUTTON1) { - Main.showProxy(); - } - } - }); - try { - tray.add(trayIcon); - } catch (AWTException ex) { - } - } - } - - public static void exit() { - Configuration.saveConfig(); - if (mainFrame != null && mainFrame.getPanel() != null) { - mainFrame.getPanel().unloadFlashPlayer(); - } - System.exit(0); - } - - public static void about() { - (new AboutDialog()).setVisible(true); - } - - public static void advancedSettings() { - (new AdvancedSettingsDialog()).setVisible(true); - } - - public static void autoCheckForUpdates() { - if (Configuration.checkForUpdatesAuto.get()) { - Calendar lastUpdatesCheckDate = Configuration.lastUpdatesCheckDate.get(); - if ((lastUpdatesCheckDate == null) || (lastUpdatesCheckDate.getTime().getTime() < Calendar.getInstance().getTime().getTime() - Configuration.checkForUpdatesDelay.get())) { - new SwingWorker() { - @Override - protected Object doInBackground() throws Exception { - checkForUpdates(); - return null; - } - }.execute(); - } - } - } - - public static boolean checkForUpdates() { - String currentVersion = ApplicationInfo.version; - if (currentVersion.equals("unknown")) { - // sometimes during development the version information is not available - return false; - } - - List accepted = new ArrayList<>(); - if (Configuration.checkForUpdatesStable.get()) { - accepted.add("stable"); - } - if (Configuration.checkForUpdatesNightly.get()) { - accepted.add("nightly"); - } - - if (accepted.isEmpty()) { - return false; - } - - String acceptVersions = String.join(",", accepted); - try { - String proxyAddress = Configuration.updateProxyAddress.get(); - URL url = new URL(ApplicationInfo.updateCheckUrl); - - URLConnection uc = null; - if (proxyAddress != null && !proxyAddress.isEmpty()) { - int port = 8080; - if (proxyAddress.contains(":")) { - String[] parts = proxyAddress.split(":"); - port = Integer.parseInt(parts[1]); - proxyAddress = parts[0]; - } - - uc = url.openConnection(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyAddress, port))); - } else { - uc = url.openConnection(); - } - uc.setRequestProperty("X-Accept-Versions", acceptVersions); - uc.setRequestProperty("X-Update-Major", "" + UPDATE_SYSTEM_MAJOR); - uc.setRequestProperty("X-Update-Minor", "" + UPDATE_SYSTEM_MINOR); - uc.setRequestProperty("User-Agent", ApplicationInfo.shortApplicationVerName); - String currentLoc = Configuration.locale.get("en"); - uc.setRequestProperty("Accept-Language", currentLoc + ("en".equals(currentLoc) ? "" : ", en;q=0.8")); - - uc.connect(); - - BufferedReader br = new BufferedReader(new InputStreamReader(uc.getInputStream())); - String s; - final java.util.List versions = new ArrayList<>(); - String header = ""; - Pattern headerPat = Pattern.compile("\\[([a-zA-Z0-9]+)\\]"); - int updateMajor; - int updateMinor; - Version ver = null; - while ((s = br.readLine()) != null) { - - Matcher m = headerPat.matcher(s); - if (m.matches()) { - header = m.group(1); - if (header.equals("version")) { - ver = new Version(); - versions.add(ver); - } - if (header.equals("noversion")) { - break; - } - } else { - if (s.contains("=")) { - String key = s.substring(0, s.indexOf('=')); - String val = s.substring(s.indexOf('=') + 1); - if ("updateSystem".equals(header)) { - if (key.equals("majorVersion")) { - updateMajor = Integer.parseInt(val); - if (updateMajor > UPDATE_SYSTEM_MAJOR) { - break; - } - } - if (key.equals("minorVersion")) { - updateMinor = Integer.parseInt(val); - } - } - if ("version".equals(header) && (ver != null)) { - if (key.equals("versionId")) { - ver.versionId = Integer.parseInt(val); - } - if (key.equals("versionName")) { - ver.versionName = val; - } - if (key.equals("nightly")) { - ver.nightly = val.equals("true"); - } - if (key.equals("revision")) { - ver.revision = val; - } - if (key.equals("build")) { - ver.build = Integer.parseInt(val); - } - if (key.equals("major")) { - ver.major = Integer.parseInt(val); - } - if (key.equals("minor")) { - ver.minor = Integer.parseInt(val); - } - if (key.equals("release")) { - ver.release = Integer.parseInt(val); - } - if (key.equals("longVersionName")) { - ver.longVersionName = val; - } - if (key.equals("releaseDate")) { - ver.releaseDate = val; - } - if (key.equals("appName")) { - ver.appName = val; - } - if (key.equals("appFullName")) { - ver.appFullName = val; - } - if (key.equals("updateLink")) { - ver.updateLink = val; - } - if (key.equals("change[]")) { - String changeType = val.substring(0, val.indexOf('|')); - String change = val.substring(val.indexOf('|') + 1); - if (!ver.changes.containsKey(changeType)) { - ver.changes.put(changeType, new ArrayList<>()); - } - List chlist = ver.changes.get(changeType); - chlist.add(change); - } - } - } - } - } - - if (!versions.isEmpty()) { - View.execInEventDispatch(() -> { - NewVersionDialog newVersionDialog = new NewVersionDialog(versions); - newVersionDialog.setVisible(true); - Configuration.lastUpdatesCheckDate.set(Calendar.getInstance()); - }); - - return true; - } - } catch (IOException | NumberFormatException ex) { - return false; - } - Configuration.lastUpdatesCheckDate.set(Calendar.getInstance()); - return false; - } - - private static FileHandler fileTxt; - - public static void clearLogFile() { - Logger logger = Logger.getLogger(""); - - FileHandler oldFileTxt = fileTxt; - fileTxt = null; - if (oldFileTxt != null) { - logger.removeHandler(fileTxt); - oldFileTxt.flush(); - oldFileTxt.close(); - } - - String fileName; - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss"); - - try { - fileName = Configuration.getFFDecHome() + File.separator + "logs" + File.separator; - if (Configuration.useDetailedLogging.get()) { - fileName += "log-" + sdf.format(new Date()) + ".txt"; - } else { - fileName += "log.txt"; - } - File f = new File(fileName).getParentFile(); - if (!f.exists()) { - f.mkdir(); - } - fileTxt = new FileHandler(fileName); - } catch (IOException | SecurityException ex) { - logger.log(Level.SEVERE, null, ex); - } - - Formatter formatterTxt = new LogFormatter(); - fileTxt.setFormatter(formatterTxt); - logger.addHandler(fileTxt); - - if (!GraphicsEnvironment.isHeadless() && ErrorLogFrame.hasInstance()) { - ErrorLogFrame.getInstance().clearErrorState(); - } - - sdf = new SimpleDateFormat("yyyy-MM-dd"); - logger.log(Level.INFO, "Date: {0}", sdf.format(new Date())); - logger.log(Level.INFO, ApplicationInfo.applicationVerName); - logger.log(Level.INFO, "{0} {1} {2}", new Object[]{ - System.getProperty("os.name"), System.getProperty("os.version"), System.getProperty("os.arch")}); - logger.log(Level.INFO, "{0} {1} {2}", new Object[]{ - System.getProperty("java.version"), System.getProperty("java.vendor"), System.getProperty("os.arch")}); - } - - public static void initLogging(boolean debug) { - try { - Logger logger = Logger.getLogger(""); - logger.setLevel(Configuration.logLevel); - - Handler[] handlers = logger.getHandlers(); - for (int i = handlers.length - 1; i >= 0; i--) { - logger.removeHandler(handlers[i]); - } - - ConsoleHandler conHan = new ConsoleHandler(); - conHan.setLevel(debug ? Level.CONFIG : Level.WARNING); - SimpleFormatter formatterTxt = new SimpleFormatter(); - conHan.setFormatter(formatterTxt); - logger.addHandler(conHan); - clearLogFile(); - - } catch (Exception ex) { - throw new RuntimeException("Problems with creating the log files"); - } - } -} +/* + * Copyright (C) 2010-2015 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui; + +import com.jpexs.decompiler.flash.ApplicationInfo; +import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFBundle; +import com.jpexs.decompiler.flash.SWFSourceInfo; +import com.jpexs.decompiler.flash.SearchMode; +import com.jpexs.decompiler.flash.SwfOpenException; +import com.jpexs.decompiler.flash.Version; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.configuration.SwfSpecificConfiguration; +import com.jpexs.decompiler.flash.console.CommandLineArgumentParser; +import com.jpexs.decompiler.flash.console.ContextMenuTools; +import com.jpexs.decompiler.flash.exporters.modes.ExeExportMode; +import com.jpexs.decompiler.flash.gui.pipes.FirstInstance; +import com.jpexs.decompiler.flash.gui.proxy.ProxyFrame; +import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.treeitems.SWFList; +import com.jpexs.helpers.Cache; +import com.jpexs.helpers.CancellableWorker; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.Path; +import com.jpexs.helpers.ProgressListener; +import com.jpexs.helpers.Stopwatch; +import com.jpexs.helpers.streams.SeekableInputStream; +import com.sun.jna.Platform; +import com.sun.jna.platform.win32.Advapi32Util; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.WinReg; +import java.awt.AWTException; +import java.awt.Frame; +import java.awt.GraphicsEnvironment; +import java.awt.MenuItem; +import java.awt.PopupMenu; +import java.awt.SystemTray; +import java.awt.TrayIcon; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map.Entry; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.logging.ConsoleHandler; +import java.util.logging.FileHandler; +import java.util.logging.Formatter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.SwingWorker; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.filechooser.FileFilter; +import org.pushingpixels.substance.api.SubstanceLookAndFeel; + +/** + * Main executable class + * + * @author JPEXS + */ +public class Main { + + protected static ProxyFrame proxyFrame; + + private static List sourceInfos = new ArrayList<>(); + + public static LoadingDialog loadingDialog; + + private static boolean working = false; + + private static TrayIcon trayIcon; + + private static MenuItem stopMenuItem; + + private static MainFrame mainFrame; + + public static final int UPDATE_SYSTEM_MAJOR = 1; + + public static final int UPDATE_SYSTEM_MINOR = 3; + + private static LoadFromMemoryFrame loadFromMemoryFrame; + + private static LoadFromCacheFrame loadFromCacheFrame; + + private static final Logger logger = Logger.getLogger(Main.class.getName()); + + public static DebugLogDialog debugDialog; + + public static boolean shouldCloseWhenClosingLoadingDialog; + + public static void ensureMainFrame() { + if (mainFrame == null) { + synchronized (Main.class) { + if (mainFrame == null) { + MainFrame frame; + if (Configuration.useRibbonInterface.get()) { + frame = new MainFrameRibbon(); + } else { + frame = new MainFrameClassic(); + } + frame.getPanel().setErrorState(ErrorLogFrame.getInstance().getErrorState()); + mainFrame = frame; + } + } + } + } + + public static MainFrame getMainFrame() { + return mainFrame; + } + + public static void loadFromCache() { + if (loadFromCacheFrame == null) { + loadFromCacheFrame = new LoadFromCacheFrame(); + } + loadFromCacheFrame.setVisible(true); + } + + public static void loadFromMemory() { + if (loadFromMemoryFrame == null) { + loadFromMemoryFrame = new LoadFromMemoryFrame(mainFrame); + } + loadFromMemoryFrame.setVisible(true); + } + + public static void setSubLimiter(boolean value) { + if (value) { + AVM2Code.toSourceLimit = Configuration.sublimiter.get(); + } else { + AVM2Code.toSourceLimit = -1; + } + } + + public static boolean isWorking() { + return working; + } + + public static void startProxy(int port) { + if (proxyFrame == null) { + proxyFrame = new ProxyFrame(mainFrame); + } + + proxyFrame.setPort(port); + addTrayIcon(); + switchProxy(); + } + + public static void showProxy() { + if (proxyFrame == null) { + proxyFrame = new ProxyFrame(mainFrame); + } + proxyFrame.setVisible(true); + proxyFrame.setState(Frame.NORMAL); + } + + public static void startWork(String name) { + startWork(name, -1, null); + } + + public static void startWork(String name, int percent) { + startWork(name, percent, null); + } + + public static void startWork(String name, CancellableWorker worker) { + startWork(name, -1, worker); + } + + public static void startWork(final String name, final int percent, final CancellableWorker worker) { + working = true; + View.execInEventDispatchLater(() -> { + if (mainFrame != null) { + mainFrame.getPanel().setWorkStatus(name, worker); + if (percent == -1) { + mainFrame.getPanel().hidePercent(); + } else { + mainFrame.getPanel().setPercent(percent); + } + } + if (loadingDialog != null) { + loadingDialog.setDetail(name); + loadingDialog.setPercent(percent); + } + if (CommandLineArgumentParser.isCommandLineMode()) { + System.out.println(name); + } + }); + } + + public static void stopWork() { + working = false; + View.execInEventDispatchLater(() -> { + if (mainFrame != null) { + mainFrame.getPanel().setWorkStatus("", null); + } + if (loadingDialog != null) { + loadingDialog.setDetail(""); + } + }); + } + + public static SWFList parseSWF(SWFSourceInfo sourceInfo) throws Exception { + SWFList result = new SWFList(); + + InputStream inputStream = sourceInfo.getInputStream(); + SWFBundle bundle = null; + FileInputStream fis = null; + if (inputStream == null) { + inputStream = new BufferedInputStream(fis = new FileInputStream(sourceInfo.getFile())); + bundle = sourceInfo.getBundle(false, SearchMode.ALL); + logger.log(Level.INFO, "Load file: {0}", sourceInfo.getFile()); + } else if (inputStream instanceof SeekableInputStream + || inputStream instanceof BufferedInputStream) { + try { + inputStream.reset(); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + logger.log(Level.INFO, "Load stream: {0}", sourceInfo.getFileTitle()); + } + + Stopwatch sw = Stopwatch.startNew(); + if (bundle != null) { + result.bundle = bundle; + result.name = new File(sourceInfo.getFileTitleOrName()).getName(); + for (Entry streamEntry : bundle.getAll().entrySet()) { + InputStream stream = streamEntry.getValue(); + stream.reset(); + CancellableWorker worker = new CancellableWorker() { + @Override + public SWF doInBackground() throws Exception { + SWF swf = new SWF(stream, null, streamEntry.getKey(), new ProgressListener() { + @Override + public void progress(int p) { + startWork(AppStrings.translate("work.reading.swf"), p); + } + }, Configuration.parallelSpeedUp.get()); + return swf; + } + }; + loadingDialog.setWroker(worker); + worker.execute(); + + try { + result.add(worker.get()); + } catch (CancellationException ex) { + logger.log(Level.WARNING, "Loading SWF {0} was cancelled.", streamEntry.getKey()); + } + } + } else { + InputStream fInputStream = inputStream; + CancellableWorker worker = new CancellableWorker() { + @Override + public SWF doInBackground() throws Exception { + SWF swf = new SWF(fInputStream, sourceInfo.getFile(), sourceInfo.getFileTitle(), new ProgressListener() { + @Override + public void progress(int p) { + startWork(AppStrings.translate("work.reading.swf"), p); + } + }, Configuration.parallelSpeedUp.get()); + return swf; + } + }; + if (loadingDialog != null) { + loadingDialog.setWroker(worker); + } + worker.execute(); + + try { + result.add(worker.get()); + } catch (CancellationException ex) { + logger.log(Level.WARNING, "Loading SWF {0} was cancelled.", sourceInfo.getFileTitleOrName()); + } + } + + if (fis != null) { + logger.log(Level.INFO, "File loaded in {0} seconds.", (sw.getElapsedMilliseconds() / 1000)); + fis.close(); + } else { + logger.log(Level.INFO, "Stream loaded in {0} seconds.", (sw.getElapsedMilliseconds() / 1000)); + } + + result.sourceInfo = sourceInfo; + for (SWF swf : result) { + logger.log(Level.INFO, ""); + logger.log(Level.INFO, "== File information =="); + logger.log(Level.INFO, "Size: {0}", Helper.formatFileSize(swf.fileSize)); + logger.log(Level.INFO, "Flash version: {0}", swf.version); + int width = (int) ((swf.displayRect.Xmax - swf.displayRect.Xmin) / SWF.unitDivisor); + int height = (int) ((swf.displayRect.Ymax - swf.displayRect.Ymin) / SWF.unitDivisor); + logger.log(Level.INFO, "Width: {0}", width); + logger.log(Level.INFO, "Height: {0}", height); + + swf.swfList = result; + swf.addEventListener(new EventListener() { + @Override + public void handleExportingEvent(String type, int index, int count, Object data) { + String text = AppStrings.translate("work.exporting"); + if (type != null && type.length() > 0) { + text += " " + type; + } + + startWork(text + " " + index + "/" + count + " " + data); + } + + @Override + public void handleExportedEvent(String type, int index, int count, Object data) { + String text = AppStrings.translate("work.exported"); + if (type != null && type.length() > 0) { + text += " " + type; + } + + startWork(text + " " + index + "/" + count + " " + data); + } + + @Override + public void handleEvent(String event, Object data) { + if (event.equals("exporting") || event.equals("exported")) { + throw new Error("Event is not supported by this handler."); + } + if (event.equals("getVariables")) { + startWork(AppStrings.translate("work.gettingvariables") + "..." + (String) data); + } + if (event.equals("deobfuscate")) { + startWork(AppStrings.translate("work.deobfuscating") + "..." + (String) data); + } + if (event.equals("rename")) { + startWork(AppStrings.translate("work.renaming") + "..." + (String) data); + } + } + }); + } + + return result; + } + + public static void saveFile(SWF swf, String outfile) throws IOException { + saveFile(swf, outfile, SaveFileMode.SAVE, null); + } + + public static void saveFile(SWF swf, String outfile, SaveFileMode mode, ExeExportMode exeExportMode) throws IOException { + if (mode == SaveFileMode.SAVEAS && !swf.swfList.isBundle()) { + swf.setFile(outfile); + swf.swfList.sourceInfo.setFile(outfile); + } + File outfileF = new File(outfile); + File tmpFile = new File(outfile + ".tmp"); + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tmpFile))) { + if (mode == SaveFileMode.EXE) { + switch (exeExportMode) { + case WRAPPER: + InputStream exeStream = View.class.getClassLoader().getResourceAsStream("com/jpexs/helpers/resource/Swf2Exe.bin"); + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = exeStream.read(buffer)) != -1) { + fos.write(buffer, 0, bytesRead); + } + int width = swf.displayRect.Xmax - swf.displayRect.Xmin; + int height = swf.displayRect.Ymax - swf.displayRect.Ymin; + fos.write(width & 0xff); + fos.write((width >> 8) & 0xff); + fos.write((width >> 16) & 0xff); + fos.write((width >> 24) & 0xff); + fos.write(height & 0xff); + fos.write((height >> 8) & 0xff); + fos.write((height >> 16) & 0xff); + fos.write((height >> 24) & 0xff); + fos.write(Configuration.saveAsExeScaleMode.get()); + break; + case PROJECTOR_WIN: + // todo + break; + case PROJECTOR_MAC: + // todo + break; + } + } + swf.saveTo(fos); + } + if (tmpFile.exists()) { + if (tmpFile.length() > 0) { + outfileF.delete(); + if (!tmpFile.renameTo(outfileF)) { + tmpFile.delete(); + throw new IOException("Cannot access " + outfile); + } + } else { + throw new IOException("Output is empty"); + } + } + } + + private static class OpenFileWorker extends SwingWorker { + + private final SWFSourceInfo[] sourceInfos; + + private final Runnable executeAfterOpen; + + public OpenFileWorker(SWFSourceInfo sourceInfo) { + this(sourceInfo, null); + } + + public OpenFileWorker(SWFSourceInfo sourceInfo, Runnable executeAfterOpen) { + this.sourceInfos = new SWFSourceInfo[]{sourceInfo}; + this.executeAfterOpen = executeAfterOpen; + } + + public OpenFileWorker(SWFSourceInfo[] sourceInfos) { + this(sourceInfos, null); + } + + public OpenFileWorker(SWFSourceInfo[] sourceInfos, Runnable executeAfterOpen) { + this.sourceInfos = sourceInfos; + this.executeAfterOpen = executeAfterOpen; + } + + @Override + protected Object doInBackground() throws Exception { + boolean first = true; + SWF firstSWF = null; + for (final SWFSourceInfo sourceInfo : sourceInfos) { + SWFList swfs = null; + try { + Main.startWork(AppStrings.translate("work.reading.swf") + "..."); + try { + swfs = parseSWF(sourceInfo); + } catch (ExecutionException ex) { + Throwable cause = ex.getCause(); + if (cause instanceof SwfOpenException) { + throw (SwfOpenException) cause; + } + + throw ex; + } + } catch (OutOfMemoryError ex) { + logger.log(Level.SEVERE, null, ex); + View.showMessageDialog(null, "Cannot load SWF file. Out of memory."); + continue; + } catch (SwfOpenException ex) { + logger.log(Level.SEVERE, null, ex); + View.showMessageDialog(null, ex.getMessage()); + continue; + } catch (Exception ex) { + logger.log(Level.SEVERE, null, ex); + View.showMessageDialog(null, "Cannot load SWF file."); + continue; + } + + final SWFList swfs1 = swfs; + final boolean first1 = first; + first = false; + if (firstSWF == null) { + firstSWF = swfs1.get(0); + } + + try { + View.execInEventDispatch(() -> { + Main.startWork(AppStrings.translate("work.creatingwindow") + "..."); + ensureMainFrame(); + mainFrame.getPanel().load(swfs1, first1); + }); + } catch (Exception ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + loadingDialog.setVisible(false); + shouldCloseWhenClosingLoadingDialog = false; + + final SWF fswf = firstSWF; + View.execInEventDispatch(() -> { + if (mainFrame != null) { + mainFrame.setVisible(true); + } + + Main.stopWork(); + + if (mainFrame != null && Configuration.gotoMainClassOnStartup.get()) { + mainFrame.getPanel().gotoDocumentClass(fswf); + } + + if (mainFrame != null && fswf != null) { + SwfSpecificConfiguration swfConf = Configuration.getSwfSpecificConfiguration(fswf.getShortFileName()); + if (swfConf != null) { + String pathStr = swfConf.lastSelectedPath; + mainFrame.getPanel().tagTree.setSelectionPathString(pathStr); + } + } + + if (executeAfterOpen != null) { + executeAfterOpen.run(); + } + }); + + return true; + } + } + + public static boolean reloadSWFs() { + CancellableWorker.cancelBackgroundThreads(); + if (Main.sourceInfos.isEmpty()) { + Helper.freeMem(); + showModeFrame(); + return true; + } else { + SWFSourceInfo[] sourceInfosCopy = new SWFSourceInfo[sourceInfos.size()]; + sourceInfos.toArray(sourceInfosCopy); + sourceInfos.clear(); + openFile(sourceInfosCopy); + return true; + } + } + + public static void reloadApp() { + if (debugDialog != null) { + debugDialog.setVisible(false); + debugDialog.dispose(); + debugDialog = null; + } + if (loadingDialog != null) { + synchronized (Main.class) { + if (loadingDialog != null) { + loadingDialog.setVisible(false); + loadingDialog.dispose(); + loadingDialog = null; + } + } + } + if (proxyFrame != null) { + proxyFrame.setVisible(false); + proxyFrame.dispose(); + proxyFrame = null; + } + if (loadFromMemoryFrame != null) { + loadFromMemoryFrame.setVisible(false); + loadFromMemoryFrame.dispose(); + loadFromMemoryFrame = null; + } + if (loadFromCacheFrame != null) { + loadFromCacheFrame.setVisible(false); + loadFromCacheFrame.dispose(); + loadFromCacheFrame = null; + } + if (mainFrame != null) { + mainFrame.setVisible(false); + mainFrame.getPanel().closeAll(); + mainFrame.dispose(); + mainFrame = null; + } + FontTag.reload(); + Cache.clearAll(); + initGui(); + reloadSWFs(); + } + + public static OpenFileResult openFile(String swfFile, String fileTitle) { + return openFile(swfFile, fileTitle, null); + } + + public static OpenFileResult openFile(String swfFile, String fileTitle, Runnable executeAfterOpen) { + try { + File file = new File(swfFile); + if (!file.exists()) { + View.showMessageDialog(null, AppStrings.translate("open.error.fileNotFound"), AppStrings.translate("open.error"), JOptionPane.ERROR_MESSAGE); + return OpenFileResult.NOT_FOUND; + } + swfFile = file.getCanonicalPath(); + Configuration.addRecentFile(swfFile); + SWFSourceInfo sourceInfo = new SWFSourceInfo(null, swfFile, fileTitle); + OpenFileResult openResult = openFile(sourceInfo); + return openResult; + } catch (IOException ex) { + View.showMessageDialog(null, AppStrings.translate("open.error.cannotOpen"), AppStrings.translate("open.error"), JOptionPane.ERROR_MESSAGE); + return OpenFileResult.ERROR; + } + } + + public static OpenFileResult openFile(SWFSourceInfo sourceInfo) { + return openFile(new SWFSourceInfo[]{sourceInfo}); + } + + public static OpenFileResult openFile(SWFSourceInfo sourceInfo, Runnable executeAfterOpen) { + return openFile(new SWFSourceInfo[]{sourceInfo}, executeAfterOpen); + } + + public static OpenFileResult openFile(SWFSourceInfo[] newSourceInfos) { + return openFile(newSourceInfos, null); + } + + public static OpenFileResult openFile(SWFSourceInfo[] newSourceInfos, Runnable executeAfterOpen) { + if (mainFrame != null && !Configuration.openMultipleFiles.get()) { + sourceInfos.clear(); + mainFrame.getPanel().closeAll(); + mainFrame.setVisible(false); + Helper.freeMem(); + } + + loadingDialog.setVisible(true); + OpenFileWorker wrk = new OpenFileWorker(newSourceInfos, executeAfterOpen); + wrk.execute(); + sourceInfos.addAll(Arrays.asList(newSourceInfos)); + return OpenFileResult.OK; + } + + public static void closeFile(SWFList swf) { + sourceInfos.remove(swf.sourceInfo); + mainFrame.getPanel().close(swf); + } + + public static void closeAll() { + sourceInfos.clear(); + mainFrame.getPanel().closeAll(); + } + + public static boolean saveFileDialog(SWF swf, final SaveFileMode mode) { + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(Configuration.lastSaveDir.get())); + String ext = ".swf"; + switch (mode) { + case SAVE: + case SAVEAS: + if (swf.getFile() != null) { + ext = Path.getExtension(swf.getFile()); + } + break; + case EXE: + ext = ".exe"; + break; + } + + FileFilter swfFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".swf")) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.swf"); + } + }; + + FileFilter gfxFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".gfx")) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.gfx"); + } + }; + + ExeExportMode exeExportMode = null; + if (mode == SaveFileMode.EXE) { + exeExportMode = Configuration.exeExportMode.get(); + String filterDescription = null; + switch (exeExportMode) { + case WRAPPER: + ext = ".exe"; + filterDescription = "filter.exe"; + break; + case PROJECTOR_WIN: + ext = ".exe"; + filterDescription = "filter.exe"; + break; + case PROJECTOR_MAC: + ext = ".dmg"; + filterDescription = "filter.dmg"; + break; + /*case PROJECTOR_LINUX: + // linux projector is compressed with tar.gz + // todo: decompress + ext = ""; + filterDescription = "filter.linuxExe"; + break;*/ + } + + String fext = ext; + String ffilterDescription = filterDescription; + FileFilter exeFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(fext)) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate(ffilterDescription); + } + }; + fc.setFileFilter(exeFilter); + } else { + if (swf.gfx) { + fc.addChoosableFileFilter(swfFilter); + fc.setFileFilter(gfxFilter); + } else { + fc.setFileFilter(swfFilter); + fc.addChoosableFileFilter(gfxFilter); + } + } + final String extension = ext; + fc.setAcceptAllFileFilterUsed(false); + JFrame f = new JFrame(); + View.setWindowIcon(f); + if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) { + File file = Helper.fixDialogFile(fc.getSelectedFile()); + FileFilter selFilter = fc.getFileFilter(); + try { + String fileName = file.getAbsolutePath(); + if (selFilter == swfFilter) { + if (!fileName.toLowerCase().endsWith(extension)) { + fileName += extension; + } + swf.gfx = false; + } + if (selFilter == gfxFilter) { + if (!fileName.toLowerCase().endsWith(".gfx")) { + fileName += ".gfx"; + } + swf.gfx = true; + } + Main.saveFile(swf, fileName, mode, exeExportMode); + Configuration.lastSaveDir.set(file.getParentFile().getAbsolutePath()); + return true; + } catch (IOException ex) { + View.showMessageDialog(null, AppStrings.translate("error.file.write")); + } + } + return false; + } + + public static boolean openFileDialog() { + JFileChooser fc = new JFileChooser(); + if (Configuration.openMultipleFiles.get()) { + fc.setMultiSelectionEnabled(true); + } + fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); + FileFilter allSupportedFilter = new FileFilter() { + private final String[] supportedExtensions = new String[]{".swf", ".gfx", ".swc", ".zip"}; + + @Override + public boolean accept(File f) { + String name = f.getName().toLowerCase(); + for (String ext : supportedExtensions) { + if (name.endsWith(ext)) { + return true; + } + } + return f.isDirectory(); + } + + @Override + public String getDescription() { + String exts = Helper.joinStrings(supportedExtensions, "*%s", "; "); + return AppStrings.translate("filter.supported") + " (" + exts + ")"; + } + }; + fc.setFileFilter(allSupportedFilter); + FileFilter swfFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".swf")) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.swf"); + } + }; + fc.addChoosableFileFilter(swfFilter); + + FileFilter swcFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".swc")) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.swc"); + } + }; + fc.addChoosableFileFilter(swcFilter); + + FileFilter gfxFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".gfx")) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.gfx"); + } + }; + fc.addChoosableFileFilter(gfxFilter); + + FileFilter zipFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".zip")) || (f.isDirectory()); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.zip"); + } + }; + fc.addChoosableFileFilter(zipFilter); + + FileFilter binaryFilter = new FileFilter() { + @Override + public boolean accept(File f) { + return true; + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.binary"); + } + }; + fc.addChoosableFileFilter(binaryFilter); + + fc.setAcceptAllFileFilterUsed(false); + JFrame f = new JFrame(); + View.setWindowIcon(f); + int returnVal = fc.showOpenDialog(f); + if (returnVal == JFileChooser.APPROVE_OPTION) { + Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); + File[] selFiles = fc.getSelectedFiles(); + for (File file : selFiles) { + File selfile = Helper.fixDialogFile(file); + Main.openFile(selfile.getAbsolutePath(), null); + } + return true; + } else { + return false; + } + } + + public static void displayErrorFrame() { + ErrorLogFrame.getInstance().setVisible(true); + } + + private static void initGui() { + if (GraphicsEnvironment.isHeadless()) { + System.err.println("Error: Your system does not support Graphic User Interface"); + exit(); + } + if (Configuration.useRibbonInterface.get()) { + View.setLookAndFeel(); + } else { + try { + UIManager.put(SubstanceLookAndFeel.COLORIZATION_FACTOR, null); + UIManager.put("Tree.expandedIcon", null); + UIManager.put("Tree.collapsedIcon", null); + UIManager.put("ColorChooserUI", null); + UIManager.put("ColorChooser.swatchesRecentSwatchSize", null); + UIManager.put("ColorChooser.swatchesSwatchSize", null); + UIManager.put("RibbonApplicationMenuPopupPanelUI", null); + UIManager.put("RibbonApplicationMenuButtonUI", null); + UIManager.put("ProgressBarUI", null); + UIManager.put("TextField.background", null); + UIManager.put("FormattedTextField.background", null); + UIManager.put("CommandButtonUI", null); + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + ErrorLogFrame.createNewInstance(); + + autoCheckForUpdates(); + offerAssociation(); + loadingDialog = new LoadingDialog(); + } + + public static void showModeFrame() { + ensureMainFrame(); + mainFrame.setVisible(true); + } + + private static void offerAssociation() { + boolean offered = Configuration.offeredAssociation.get(); + if (!offered) { + if (Platform.isWindows()) { + if ((!ContextMenuTools.isAddedToContextMenu()) && View.showConfirmDialog(null, "Do you want to add FFDec to context menu of SWF files?\n(Can be changed later from main menu)", "Context menu", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { + ContextMenuTools.addToContextMenu(true, false); + } + } + + Configuration.offeredAssociation.set(true); + } + } + + public static void initLang() { + if (GraphicsEnvironment.isHeadless()) { //No GUI in OS + return; + } + if (!Configuration.locale.hasValue()) { + if (Platform.isWindows()) { + //Load from Installer + String uninstKey = "{E618D276-6596-41F4-8A98-447D442A77DB}_is1"; + uninstKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + uninstKey; + try { + if (Advapi32Util.registryKeyExists(WinReg.HKEY_LOCAL_MACHINE, uninstKey)) { + if (Advapi32Util.registryValueExists(WinReg.HKEY_LOCAL_MACHINE, uninstKey, "NSIS: Language")) { + String installedLoc = Advapi32Util.registryGetStringValue(WinReg.HKEY_LOCAL_MACHINE, uninstKey, "NSIS: Language"); + int lcid = Integer.parseInt(installedLoc); + char buf[] = new char[9]; + int cnt = Kernel32.INSTANCE.GetLocaleInfo(lcid, Kernel32.LOCALE_SISO639LANGNAME, buf, 9); + String langCode = new String(buf, 0, cnt).trim().toLowerCase(); + + cnt = Kernel32.INSTANCE.GetLocaleInfo(lcid, Kernel32.LOCALE_SISO3166CTRYNAME, buf, 9); + String countryCode = new String(buf, 0, cnt).trim().toLowerCase(); + + List langs = Arrays.asList(SelectLanguageDialog.getAvailableLanguages()); + for (int i = 0; i < langs.size(); i++) { + langs.set(i, langs.get(i).toLowerCase()); + } + + String selectedLang = null; + + if (langs.contains(langCode + "-" + countryCode)) { + selectedLang = SelectLanguageDialog.getAvailableLanguages()[langs.indexOf(langCode + "-" + countryCode)]; + } else if (langs.contains(langCode)) { + selectedLang = SelectLanguageDialog.getAvailableLanguages()[langs.indexOf(langCode)]; + } + if (selectedLang != null) { + Configuration.locale.set(selectedLang); + } + } + } + } catch (Exception ex) { + //ignore + } + } + } + Locale.setDefault(Locale.forLanguageTag(Configuration.locale.get())); + AppStrings.updateLanguage(); + + try { + Class cl = Class.forName("org.pushingpixels.substance.api.SubstanceLookAndFeel"); + Field field = cl.getDeclaredField("LABEL_BUNDLE"); + field.setAccessible(true); + field.set(null, null); + } catch (Throwable ex) { + logger.log(Level.SEVERE, null, ex); + } + + UIManager.put("OptionPane.okButtonText", AppStrings.translate("button.ok")); + UIManager.put("OptionPane.yesButtonText", AppStrings.translate("button.yes")); + UIManager.put("OptionPane.noButtonText", AppStrings.translate("button.no")); + UIManager.put("OptionPane.cancelButtonText", AppStrings.translate("button.cancel")); + UIManager.put("OptionPane.messageDialogTitle", AppStrings.translate("dialog.message.title")); + UIManager.put("OptionPane.titleText", AppStrings.translate("dialog.select.title")); + + UIManager.put("FileChooser.acceptAllFileFilterText", AppStrings.translate("FileChooser.acceptAllFileFilterText")); + UIManager.put("FileChooser.lookInLabelText", AppStrings.translate("FileChooser.lookInLabelText")); + UIManager.put("FileChooser.cancelButtonText", AppStrings.translate("button.cancel")); + UIManager.put("FileChooser.cancelButtonToolTipText", AppStrings.translate("button.cancel")); + UIManager.put("FileChooser.openButtonText", AppStrings.translate("FileChooser.openButtonText")); + UIManager.put("FileChooser.openButtonToolTipText", AppStrings.translate("FileChooser.openButtonToolTipText")); + UIManager.put("FileChooser.filesOfTypeLabelText", AppStrings.translate("FileChooser.filesOfTypeLabelText")); + UIManager.put("FileChooser.fileNameLabelText", AppStrings.translate("FileChooser.fileNameLabelText")); + UIManager.put("FileChooser.listViewButtonToolTipText", AppStrings.translate("FileChooser.listViewButtonToolTipText")); + UIManager.put("FileChooser.listViewButtonAccessibleName", AppStrings.translate("FileChooser.listViewButtonAccessibleName")); + UIManager.put("FileChooser.detailsViewButtonToolTipText", AppStrings.translate("FileChooser.detailsViewButtonToolTipText")); + UIManager.put("FileChooser.detailsViewButtonAccessibleName", AppStrings.translate("FileChooser.detailsViewButtonAccessibleName")); + UIManager.put("FileChooser.upFolderToolTipText", AppStrings.translate("FileChooser.upFolderToolTipText")); + UIManager.put("FileChooser.upFolderAccessibleName", AppStrings.translate("FileChooser.upFolderAccessibleName")); + UIManager.put("FileChooser.homeFolderToolTipText", AppStrings.translate("FileChooser.homeFolderToolTipText")); + UIManager.put("FileChooser.homeFolderAccessibleName", AppStrings.translate("FileChooser.homeFolderAccessibleName")); + UIManager.put("FileChooser.fileNameHeaderText", AppStrings.translate("FileChooser.fileNameHeaderText")); + UIManager.put("FileChooser.fileSizeHeaderText", AppStrings.translate("FileChooser.fileSizeHeaderText")); + UIManager.put("FileChooser.fileTypeHeaderText", AppStrings.translate("FileChooser.fileTypeHeaderText")); + UIManager.put("FileChooser.fileDateHeaderText", AppStrings.translate("FileChooser.fileDateHeaderText")); + UIManager.put("FileChooser.fileAttrHeaderText", AppStrings.translate("FileChooser.fileAttrHeaderText")); + UIManager.put("FileChooser.openDialogTitleText", AppStrings.translate("FileChooser.openDialogTitleText")); + UIManager.put("FileChooser.directoryDescriptionText", AppStrings.translate("FileChooser.directoryDescriptionText")); + UIManager.put("FileChooser.directoryOpenButtonText", AppStrings.translate("FileChooser.directoryOpenButtonText")); + UIManager.put("FileChooser.directoryOpenButtonToolTipText", AppStrings.translate("FileChooser.directoryOpenButtonToolTipText")); + UIManager.put("FileChooser.fileDescriptionText", AppStrings.translate("FileChooser.fileDescriptionText")); + UIManager.put("FileChooser.fileNameLabelText", AppStrings.translate("FileChooser.fileNameLabelText")); + UIManager.put("FileChooser.helpButtonText", AppStrings.translate("FileChooser.helpButtonText")); + UIManager.put("FileChooser.helpButtonToolTipText", AppStrings.translate("FileChooser.helpButtonToolTipText")); + UIManager.put("FileChooser.newFolderAccessibleName", AppStrings.translate("FileChooser.newFolderAccessibleName")); + UIManager.put("FileChooser.newFolderErrorText", AppStrings.translate("FileChooser.newFolderErrorText")); + UIManager.put("FileChooser.newFolderToolTipText", AppStrings.translate("FileChooser.newFolderToolTipText")); + UIManager.put("FileChooser.other.newFolder", AppStrings.translate("FileChooser.other.newFolder")); + UIManager.put("FileChooser.other.newFolder.subsequent", AppStrings.translate("FileChooser.other.newFolder.subsequent")); + UIManager.put("FileChooser.win32.newFolder", AppStrings.translate("FileChooser.win32.newFolder")); + UIManager.put("FileChooser.win32.newFolder.subsequent", AppStrings.translate("FileChooser.win32.newFolder.subsequent")); + UIManager.put("FileChooser.saveButtonText", AppStrings.translate("FileChooser.saveButtonText")); + UIManager.put("FileChooser.saveButtonToolTipText", AppStrings.translate("FileChooser.saveButtonToolTipText")); + UIManager.put("FileChooser.saveDialogTitleText", AppStrings.translate("FileChooser.saveDialogTitleText")); + UIManager.put("FileChooser.saveInLabelText", AppStrings.translate("FileChooser.saveInLabelText")); + UIManager.put("FileChooser.updateButtonText", AppStrings.translate("FileChooser.updateButtonText")); + UIManager.put("FileChooser.updateButtonToolTipText", AppStrings.translate("FileChooser.updateButtonToolTipText")); + + UIManager.put("FileChooser.detailsViewActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.detailsViewActionLabel.textAndMnemonic")); + UIManager.put("FileChooser.detailsViewButtonToolTip.textAndMnemonic", AppStrings.translate("FileChooser.detailsViewButtonToolTip.textAndMnemonic")); + UIManager.put("FileChooser.fileAttrHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileAttrHeader.textAndMnemonic")); + UIManager.put("FileChooser.fileDateHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileDateHeader.textAndMnemonic")); + UIManager.put("FileChooser.fileNameHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileNameHeader.textAndMnemonic")); + UIManager.put("FileChooser.fileNameLabel.textAndMnemonic", AppStrings.translate("FileChooser.fileNameLabel.textAndMnemonic")); + UIManager.put("FileChooser.fileSizeHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileSizeHeader.textAndMnemonic")); + UIManager.put("FileChooser.fileTypeHeader.textAndMnemonic", AppStrings.translate("FileChooser.fileTypeHeader.textAndMnemonic")); + UIManager.put("FileChooser.filesOfTypeLabel.textAndMnemonic", AppStrings.translate("FileChooser.filesOfTypeLabel.textAndMnemonic")); + UIManager.put("FileChooser.folderNameLabel.textAndMnemonic", AppStrings.translate("FileChooser.folderNameLabel.textAndMnemonic")); + UIManager.put("FileChooser.homeFolderToolTip.textAndMnemonic", AppStrings.translate("FileChooser.homeFolderToolTip.textAndMnemonic")); + UIManager.put("FileChooser.listViewActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.listViewActionLabel.textAndMnemonic")); + UIManager.put("FileChooser.listViewButtonToolTip.textAndMnemonic", AppStrings.translate("FileChooser.listViewButtonToolTip.textAndMnemonic")); + UIManager.put("FileChooser.lookInLabel.textAndMnemonic", AppStrings.translate("FileChooser.lookInLabel.textAndMnemonic")); + UIManager.put("FileChooser.newFolderActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.newFolderActionLabel.textAndMnemonic")); + UIManager.put("FileChooser.newFolderToolTip.textAndMnemonic", AppStrings.translate("FileChooser.newFolderToolTip.textAndMnemonic")); + UIManager.put("FileChooser.refreshActionLabel.textAndMnemonic", AppStrings.translate("FileChooser.refreshActionLabel.textAndMnemonic")); + UIManager.put("FileChooser.saveInLabel.textAndMnemonic", AppStrings.translate("FileChooser.saveInLabel.textAndMnemonic")); + UIManager.put("FileChooser.upFolderToolTip.textAndMnemonic", AppStrings.translate("FileChooser.upFolderToolTip.textAndMnemonic")); + UIManager.put("FileChooser.viewMenuButtonAccessibleName", AppStrings.translate("FileChooser.viewMenuButtonAccessibleName")); + UIManager.put("FileChooser.viewMenuButtonToolTipText", AppStrings.translate("FileChooser.viewMenuButtonToolTipText")); + UIManager.put("FileChooser.viewMenuLabel.textAndMnemonic", AppStrings.translate("FileChooser.viewMenuLabel.textAndMnemonic")); + UIManager.put("FileChooser.newFolderActionLabelText", AppStrings.translate("FileChooser.newFolderActionLabelText")); + UIManager.put("FileChooser.listViewActionLabelText", AppStrings.translate("FileChooser.listViewActionLabelText")); + UIManager.put("FileChooser.detailsViewActionLabelText", AppStrings.translate("FileChooser.detailsViewActionLabelText")); + UIManager.put("FileChooser.refreshActionLabelText", AppStrings.translate("FileChooser.refreshActionLabelText")); + UIManager.put("FileChooser.sortMenuLabelText", AppStrings.translate("FileChooser.sortMenuLabelText")); + UIManager.put("FileChooser.viewMenuLabelText", AppStrings.translate("FileChooser.viewMenuLabelText")); + UIManager.put("FileChooser.fileSizeKiloBytes", AppStrings.translate("FileChooser.fileSizeKiloBytes")); + UIManager.put("FileChooser.fileSizeMegaBytes", AppStrings.translate("FileChooser.fileSizeMegaBytes")); + UIManager.put("FileChooser.fileSizeGigaBytes", AppStrings.translate("FileChooser.fileSizeGigaBytes")); + UIManager.put("FileChooser.folderNameLabelText", AppStrings.translate("FileChooser.folderNameLabelText")); + + UIManager.put("ColorChooser.okText", AppStrings.translate("ColorChooser.okText")); + UIManager.put("ColorChooser.cancelText", AppStrings.translate("ColorChooser.cancelText")); + UIManager.put("ColorChooser.resetText", AppStrings.translate("ColorChooser.resetText")); + UIManager.put("ColorChooser.previewText", AppStrings.translate("ColorChooser.previewText")); + UIManager.put("ColorChooser.swatchesNameText", AppStrings.translate("ColorChooser.swatchesNameText")); + UIManager.put("ColorChooser.swatchesRecentText", AppStrings.translate("ColorChooser.swatchesRecentText")); + UIManager.put("ColorChooser.sampleText", AppStrings.translate("ColorChooser.sampleText")); + + Helper.decompilationErrorAdd = AppStrings.translate(Configuration.autoDeobfuscate.get() ? "deobfuscation.comment.failed" : "deobfuscation.comment.tryenable"); + } + + /** + * Clear old FFDec/JavactiveX temp files + */ + private static void clearTemp() { + String tempDirPath = System.getProperty("java.io.tmpdir"); + if (tempDirPath == null) { + return; + } + File tempDir = new File(tempDirPath); + if (!tempDir.exists()) { + return; + } + File delFiles[] = tempDir.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + return name.matches("ffdec_cache.*\\.tmp") || name.matches("javactivex_.*\\.exe") || name.matches("temp[0-9]+\\.swf") || name.matches("ffdec_view_.*\\.swf"); + } + }); + for (File f : delFiles) { + try { + f.delete(); + } catch (Exception ex) { + //ignore + } + } + } + + /** + * @param args the command line arguments + * @throws IOException On error + */ + public static void main(String[] args) throws IOException { + + clearTemp(); + String pluginPath = Configuration.pluginPath.get(); + if (pluginPath != null && !pluginPath.isEmpty()) { + try { + SWFDecompilerPlugin.loadPlugin(pluginPath); + } catch (Throwable e) { + View.showMessageDialog(null, "Failed to load plugin: " + pluginPath); + } + } + + AppStrings.setResourceClass(MainFrame.class); + initLogging(Configuration.debugMode.get()); + initLang(); + + if (Configuration.cacheOnDisk.get()) { + Cache.setStorageType(Cache.STORAGE_FILES); + } else { + Cache.setStorageType(Cache.STORAGE_MEMORY); + } + + if (args.length == 0) { + View.execInEventDispatch(() -> { + initGui(); + if (Configuration.allowOnlyOneInstance.get() && FirstInstance.focus()) { //Try to focus first instance + Main.exit(); + } else { + showModeFrame(); + reloadLastSession(); + } + }); + } else { + String[] filesToOpen = CommandLineArgumentParser.parseArguments(args); + if (filesToOpen != null && filesToOpen.length > 0) { + View.execInEventDispatch(() -> { + initGui(); + shouldCloseWhenClosingLoadingDialog = true; + if (Configuration.allowOnlyOneInstance.get() && FirstInstance.openFiles(Arrays.asList(filesToOpen))) { //Try to open in first instance + Main.exit(); + } else { + for (String fileToOpen : filesToOpen) { + openFile(fileToOpen, null); + } + } + }); + } + } + } + + private static void reloadLastSession() { + if (Configuration.saveSessionOnExit.get()) { + String lastSession = Configuration.lastSessionFiles.get(); + if (lastSession != null && lastSession.length() > 0) { + String[] filesToOpen = lastSession.split(File.pathSeparator, -1); + List exfiles = new ArrayList<>(); + for (int i = 0; i < filesToOpen.length; i++) { + if (new File(filesToOpen[i]).exists()) { + exfiles.add(filesToOpen[i]); + } + } + SWFSourceInfo[] sourceInfos = new SWFSourceInfo[exfiles.size()]; + for (int i = 0; i < exfiles.size(); i++) { + sourceInfos[i] = new SWFSourceInfo(null, exfiles.get(i), null); + } + if (sourceInfos.length > 0) { + openFile(sourceInfos, () -> { + mainFrame.getPanel().tagTree.setSelectionPathString(Configuration.lastSessionSelection.get()); + }); + } + } + } + } + + public static String tempFile(String url) throws IOException { + File f = new File(Configuration.getFFDecHome() + "saved" + File.separator); + Path.createDirectorySafe(f); + return Configuration.getFFDecHome() + "saved" + File.separator + "asdec_" + Integer.toHexString(url.hashCode()) + ".tmp"; + } + + public static void removeTrayIcon() { + if (SystemTray.isSupported()) { + SystemTray tray = SystemTray.getSystemTray(); + if (trayIcon != null) { + tray.remove(trayIcon); + trayIcon = null; + } + } + } + + public static void switchProxy() { + proxyFrame.switchState(); + if (stopMenuItem != null) { + if (proxyFrame.isRunning()) { + stopMenuItem.setLabel(AppStrings.translate("proxy.stop")); + } else { + stopMenuItem.setLabel(AppStrings.translate("proxy.start")); + } + } + } + + public static void addTrayIcon() { + if (trayIcon != null) { + return; + } + if (SystemTray.isSupported()) { + SystemTray tray = SystemTray.getSystemTray(); + trayIcon = new TrayIcon(View.loadImage("proxy16"), ApplicationInfo.VENDOR + " " + ApplicationInfo.SHORT_APPLICATION_NAME + " " + AppStrings.translate("proxy")); + trayIcon.setImageAutoSize(true); + PopupMenu trayPopup = new PopupMenu(); + + ActionListener trayListener = new ActionListener() { + /** + * Invoked when an action occurs. + */ + @Override + public void actionPerformed(ActionEvent e) { + if (e.getActionCommand().equals("EXIT")) { + Main.exit(); + } + if (e.getActionCommand().equals("SHOW")) { + Main.showProxy(); + } + if (e.getActionCommand().equals("SWITCH")) { + Main.switchProxy(); + } + } + }; + + MenuItem showMenuItem = new MenuItem(AppStrings.translate("proxy.show")); + showMenuItem.setActionCommand("SHOW"); + showMenuItem.addActionListener(trayListener); + trayPopup.add(showMenuItem); + stopMenuItem = new MenuItem(AppStrings.translate("proxy.start")); + stopMenuItem.setActionCommand("SWITCH"); + stopMenuItem.addActionListener(trayListener); + trayPopup.add(stopMenuItem); + trayPopup.addSeparator(); + MenuItem exitMenuItem = new MenuItem(AppStrings.translate("exit")); + exitMenuItem.setActionCommand("EXIT"); + exitMenuItem.addActionListener(trayListener); + trayPopup.add(exitMenuItem); + + trayIcon.setPopupMenu(trayPopup); + trayIcon.addMouseListener(new MouseAdapter() { + /** + * {@inheritDoc} + */ + @Override + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + Main.showProxy(); + } + } + }); + try { + tray.add(trayIcon); + } catch (AWTException ex) { + } + } + } + + public static void exit() { + Configuration.saveConfig(); + if (mainFrame != null && mainFrame.getPanel() != null) { + mainFrame.getPanel().unloadFlashPlayer(); + } + System.exit(0); + } + + public static void about() { + (new AboutDialog()).setVisible(true); + } + + public static void advancedSettings() { + (new AdvancedSettingsDialog()).setVisible(true); + } + + public static void autoCheckForUpdates() { + if (Configuration.checkForUpdatesAuto.get()) { + Calendar lastUpdatesCheckDate = Configuration.lastUpdatesCheckDate.get(); + if ((lastUpdatesCheckDate == null) || (lastUpdatesCheckDate.getTime().getTime() < Calendar.getInstance().getTime().getTime() - Configuration.checkForUpdatesDelay.get())) { + new SwingWorker() { + @Override + protected Object doInBackground() throws Exception { + checkForUpdates(); + return null; + } + }.execute(); + } + } + } + + public static boolean checkForUpdates() { + String currentVersion = ApplicationInfo.version; + if (currentVersion.equals("unknown")) { + // sometimes during development the version information is not available + return false; + } + + List accepted = new ArrayList<>(); + if (Configuration.checkForUpdatesStable.get()) { + accepted.add("stable"); + } + if (Configuration.checkForUpdatesNightly.get()) { + accepted.add("nightly"); + } + + if (accepted.isEmpty()) { + return false; + } + + String acceptVersions = String.join(",", accepted); + try { + String proxyAddress = Configuration.updateProxyAddress.get(); + URL url = new URL(ApplicationInfo.updateCheckUrl); + + URLConnection uc = null; + if (proxyAddress != null && !proxyAddress.isEmpty()) { + int port = 8080; + if (proxyAddress.contains(":")) { + String[] parts = proxyAddress.split(":"); + port = Integer.parseInt(parts[1]); + proxyAddress = parts[0]; + } + + uc = url.openConnection(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyAddress, port))); + } else { + uc = url.openConnection(); + } + uc.setRequestProperty("X-Accept-Versions", acceptVersions); + uc.setRequestProperty("X-Update-Major", "" + UPDATE_SYSTEM_MAJOR); + uc.setRequestProperty("X-Update-Minor", "" + UPDATE_SYSTEM_MINOR); + uc.setRequestProperty("User-Agent", ApplicationInfo.shortApplicationVerName); + String currentLoc = Configuration.locale.get("en"); + uc.setRequestProperty("Accept-Language", currentLoc + ("en".equals(currentLoc) ? "" : ", en;q=0.8")); + + uc.connect(); + + BufferedReader br = new BufferedReader(new InputStreamReader(uc.getInputStream())); + String s; + final java.util.List versions = new ArrayList<>(); + String header = ""; + Pattern headerPat = Pattern.compile("\\[([a-zA-Z0-9]+)\\]"); + int updateMajor; + int updateMinor; + Version ver = null; + while ((s = br.readLine()) != null) { + + Matcher m = headerPat.matcher(s); + if (m.matches()) { + header = m.group(1); + if (header.equals("version")) { + ver = new Version(); + versions.add(ver); + } + if (header.equals("noversion")) { + break; + } + } else { + if (s.contains("=")) { + String key = s.substring(0, s.indexOf('=')); + String val = s.substring(s.indexOf('=') + 1); + if ("updateSystem".equals(header)) { + if (key.equals("majorVersion")) { + updateMajor = Integer.parseInt(val); + if (updateMajor > UPDATE_SYSTEM_MAJOR) { + break; + } + } + if (key.equals("minorVersion")) { + updateMinor = Integer.parseInt(val); + } + } + if ("version".equals(header) && (ver != null)) { + if (key.equals("versionId")) { + ver.versionId = Integer.parseInt(val); + } + if (key.equals("versionName")) { + ver.versionName = val; + } + if (key.equals("nightly")) { + ver.nightly = val.equals("true"); + } + if (key.equals("revision")) { + ver.revision = val; + } + if (key.equals("build")) { + ver.build = Integer.parseInt(val); + } + if (key.equals("major")) { + ver.major = Integer.parseInt(val); + } + if (key.equals("minor")) { + ver.minor = Integer.parseInt(val); + } + if (key.equals("release")) { + ver.release = Integer.parseInt(val); + } + if (key.equals("longVersionName")) { + ver.longVersionName = val; + } + if (key.equals("releaseDate")) { + ver.releaseDate = val; + } + if (key.equals("appName")) { + ver.appName = val; + } + if (key.equals("appFullName")) { + ver.appFullName = val; + } + if (key.equals("updateLink")) { + ver.updateLink = val; + } + if (key.equals("change[]")) { + String changeType = val.substring(0, val.indexOf('|')); + String change = val.substring(val.indexOf('|') + 1); + if (!ver.changes.containsKey(changeType)) { + ver.changes.put(changeType, new ArrayList<>()); + } + List chlist = ver.changes.get(changeType); + chlist.add(change); + } + } + } + } + } + + if (!versions.isEmpty()) { + View.execInEventDispatch(() -> { + NewVersionDialog newVersionDialog = new NewVersionDialog(versions); + newVersionDialog.setVisible(true); + Configuration.lastUpdatesCheckDate.set(Calendar.getInstance()); + }); + + return true; + } + } catch (IOException | NumberFormatException ex) { + return false; + } + Configuration.lastUpdatesCheckDate.set(Calendar.getInstance()); + return false; + } + + private static FileHandler fileTxt; + + public static void clearLogFile() { + Logger logger = Logger.getLogger(""); + + FileHandler oldFileTxt = fileTxt; + fileTxt = null; + if (oldFileTxt != null) { + logger.removeHandler(fileTxt); + oldFileTxt.flush(); + oldFileTxt.close(); + } + + String fileName; + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss"); + + try { + fileName = Configuration.getFFDecHome() + File.separator + "logs" + File.separator; + if (Configuration.useDetailedLogging.get()) { + fileName += "log-" + sdf.format(new Date()) + ".txt"; + } else { + fileName += "log.txt"; + } + File f = new File(fileName).getParentFile(); + if (!f.exists()) { + f.mkdir(); + } + fileTxt = new FileHandler(fileName); + } catch (IOException | SecurityException ex) { + logger.log(Level.SEVERE, null, ex); + } + + Formatter formatterTxt = new LogFormatter(); + fileTxt.setFormatter(formatterTxt); + logger.addHandler(fileTxt); + + if (!GraphicsEnvironment.isHeadless() && ErrorLogFrame.hasInstance()) { + ErrorLogFrame.getInstance().clearErrorState(); + } + + sdf = new SimpleDateFormat("yyyy-MM-dd"); + logger.log(Level.INFO, "Date: {0}", sdf.format(new Date())); + logger.log(Level.INFO, ApplicationInfo.applicationVerName); + logger.log(Level.INFO, "{0} {1} {2}", new Object[]{ + System.getProperty("os.name"), System.getProperty("os.version"), System.getProperty("os.arch")}); + logger.log(Level.INFO, "{0} {1} {2}", new Object[]{ + System.getProperty("java.version"), System.getProperty("java.vendor"), System.getProperty("os.arch")}); + } + + public static void initLogging(boolean debug) { + try { + Logger logger = Logger.getLogger(""); + logger.setLevel(Configuration.logLevel); + + Handler[] handlers = logger.getHandlers(); + for (int i = handlers.length - 1; i >= 0; i--) { + logger.removeHandler(handlers[i]); + } + + ConsoleHandler conHan = new ConsoleHandler(); + conHan.setLevel(debug ? Level.CONFIG : Level.WARNING); + SimpleFormatter formatterTxt = new SimpleFormatter(); + conHan.setFormatter(formatterTxt); + logger.addHandler(conHan); + clearLogFile(); + + } catch (Exception ex) { + throw new RuntimeException("Problems with creating the log files"); + } + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index e274eaa7d..a8008e4d4 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -2885,6 +2885,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } catch (CancellationException ex) { getABCPanel().decompiledTextArea.setText("// " + AppStrings.translate("work.canceled")); } catch (Exception ex) { + Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, "Error", ex); getABCPanel().decompiledTextArea.setText("// " + AppStrings.translate("decompilationError") + ": " + ex); } }); @@ -2892,6 +2893,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se }; worker.execute(); setSourceWorker = worker; + Main.startWork(translate("work.decompiling") + "...", worker); } diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java index 64ff16596..b459941bd 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java @@ -741,11 +741,11 @@ public class ABCPanel extends JPanel implements ItemListener, SearchListener packs = abc.script_info.get(oldIndex).getPacks(abc, oldIndex, null, pack.allABCs); - if (!packs.isEmpty()) { - oldSp = packs.get(0).getClassPath().toString(); - } + String oldSp = pack.getClassPath().toString(); + /*List packs = abc.script_info.get(oldIndex).getPacks(abc, oldIndex, null, pack.allABCs); + if (!packs.isEmpty()) { + + }*/ String as = decompiledTextArea.getText(); abc.replaceScriptPack(pack, as); diff --git a/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java b/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java index 4880a0d34..0d7e649ff 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/DecompiledEditorPane.java @@ -275,7 +275,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL Highlighting ch = Highlighting.searchPos(classHighlights, pos); int cindex = (int) ch.getProperties().index; ABC abc = getABC(); - type.setVal(abc.instance_info.get(cindex).getName(abc.constants).getNameWithNamespace(abc.constants, true)); + type.setVal(abc.instance_info.get(cindex).getName(abc.constants).getNameWithNamespace(abc.constants).toString()); return ch.startPos; } @@ -356,7 +356,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL traitIndex.setVal(j); classTrait.setVal(false); multinameIndex.setVal(tr.name_index); - currentType = ii.getName(a.constants).getNameWithNamespace(a.constants, true); + currentType = ii.getName(a.constants).getNameWithNamespace(a.constants).toString(); found = true; break loopi; } @@ -371,7 +371,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL traitIndex.setVal(j); classTrait.setVal(true); multinameIndex.setVal(tr.name_index); - currentType = ii.getName(a.constants).getNameWithNamespace(a.constants, true); + currentType = ii.getName(a.constants).getNameWithNamespace(a.constants).toString(); found = true; break loopi; } @@ -465,7 +465,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL for (int i = 1; i < abc.constants.constant_multiname.size(); i++) { Multiname m = abc.constants.constant_multiname.get(i); if (m != null) { - if (typeName.equals(m.getNameWithNamespace(abc.constants, true))) { + if (typeName.equals(m.getNameWithNamespace(abc.constants).toString())) { return i; } } @@ -520,7 +520,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL if (tm != null) { String name = ""; if (classIndex > -1) { - name = abc.instance_info.get(classIndex).getName(abc.constants).getNameWithNamespace(abc.constants, false); + name = abc.instance_info.get(classIndex).getName(abc.constants).getNameWithNamespace(abc.constants).toPrintableString(); } Trait currentTrait = null; @@ -571,7 +571,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL } currentMethodHighlight = null; currentTrait = null; - String name = abc.instance_info.get(classIndex).getName(abc.constants).getNameWithNamespace(abc.constants, false); + String name = abc.instance_info.get(classIndex).getName(abc.constants).getNameWithNamespace(abc.constants).toPrintableString(); currentTrait = getCurrentTrait(); isStatic = abc.isStaticTraitId(classIndex, lastTraitIndex); if (currentTrait != null) { diff --git a/src/com/jpexs/decompiler/flash/gui/abc/UsageFrame.java b/src/com/jpexs/decompiler/flash/gui/abc/UsageFrame.java index 109b909ca..21a398abd 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/UsageFrame.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/UsageFrame.java @@ -1,179 +1,179 @@ -/* - * Copyright (C) 2010-2015 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui.abc; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.usages.InsideClassMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MethodMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.TraitMultinameUsage; -import com.jpexs.decompiler.flash.gui.AppDialog; -import com.jpexs.decompiler.flash.gui.View; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Container; -import java.awt.FlowLayout; -import java.awt.event.ActionEvent; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.util.List; -import javax.swing.JButton; -import javax.swing.JList; -import javax.swing.JPanel; -import javax.swing.JScrollPane; - -/** - * - * @author JPEXS - */ -public class UsageFrame extends AppDialog implements MouseListener { - - private final JButton gotoButton = new JButton(translate("button.goto")); - - private final JButton cancelButton = new JButton(translate("button.cancel")); - - private final JList usageList; - - private final UsageListModel usageListModel; - - private final ABCPanel abcPanel; - - public UsageFrame(ABC abc, int multinameIndex, ABCPanel abcPanel, boolean definitions) { - super(abcPanel.getMainPanel().getMainFrame().getWindow()); - this.abcPanel = abcPanel; - List usages = definitions ? abc.findMultinameDefinition(multinameIndex) : abc.findMultinameUsage(multinameIndex); - Multiname m = abc.constants.constant_multiname.get(multinameIndex); - if (m.namespace_index > 0 && abc.constants.constant_namespace.get(m.namespace_index).kind != Namespace.KIND_PRIVATE) { - for (ABCContainerTag at : abc.getAbcTags()) { - ABC a = at.getABC(); - if (a == abc) { - continue; - } - int mid = a.constants.getMultinameId(m, false); - if (mid > 0) { - usages.addAll(definitions ? a.findMultinameDefinition(mid) : a.findMultinameUsage(mid)); - } - } - } - usageListModel = new UsageListModel(); - for (MultinameUsage u : usages) { - usageListModel.addElement(u); - } - usageList = new JList<>(usageListModel); - usageList.setBackground(Color.white); - gotoButton.addActionListener(this::gotoButtonActionPerformed); - cancelButton.addActionListener(this::cancelButtonActionPerformed); - - JPanel buttonsPanel = new JPanel(); - buttonsPanel.setLayout(new FlowLayout()); - buttonsPanel.add(gotoButton); - buttonsPanel.add(cancelButton); - - usageList.addMouseListener(this); - Container cont = getContentPane(); - cont.setLayout(new BorderLayout()); - cont.add(new JScrollPane(usageList), BorderLayout.CENTER); - cont.add(buttonsPanel, BorderLayout.SOUTH); - setSize(400, 300); - setTitle((definitions ? translate("dialog.title.declaration") : translate("dialog.title")) + abc.constants.getMultiname(multinameIndex).getNameWithNamespace(abc.constants, false)); - View.centerScreen(this); - View.setWindowIcon(this); - } - - public static void gotoUsage(final ABCPanel abcPanel, final MultinameUsage usage) { - if (usage instanceof InsideClassMultinameUsage) { - final InsideClassMultinameUsage icu = (InsideClassMultinameUsage) usage; - - Runnable settrait = new Runnable() { - - @Override - public void run() { - abcPanel.decompiledTextArea.removeScriptListener(this); - abcPanel.decompiledTextArea.setClassIndex(icu.classIndex); - if (usage instanceof TraitMultinameUsage) { - TraitMultinameUsage tmu = (TraitMultinameUsage) usage; - int traitIndex; - if (tmu.parentTraitIndex > -1) { - traitIndex = tmu.parentTraitIndex; - } else { - traitIndex = tmu.traitIndex; - } - if (!tmu.isStatic) { - traitIndex += abcPanel.abc.class_info.get(tmu.classIndex).static_traits.traits.size(); - } - if (tmu instanceof MethodMultinameUsage) { - MethodMultinameUsage mmu = (MethodMultinameUsage) usage; - if (mmu.isInitializer == true) { - traitIndex = abcPanel.abc.class_info.get(mmu.classIndex).static_traits.traits.size() + abcPanel.abc.instance_info.get(mmu.classIndex).instance_traits.traits.size() + (mmu.isStatic ? 1 : 0); - } - } - abcPanel.decompiledTextArea.gotoTrait(traitIndex); - } - } - }; - - if (abcPanel.decompiledTextArea.getClassIndex() == icu.classIndex && abcPanel.abc == icu.abc) { - settrait.run(); - } else { - abcPanel.decompiledTextArea.addScriptListener(settrait); - abcPanel.hilightScript(abcPanel.getSwf(), abcPanel.abc.instance_info.get(icu.classIndex).getName(abcPanel.abc.constants).getNameWithNamespace(abcPanel.abc.constants, false)); - } - } - } - - private void gotoUsage() { - if (usageList.getSelectedIndex() != -1) { - MultinameUsage usage = usageListModel.getUsage(usageList.getSelectedIndex()); - gotoUsage(abcPanel, usage); - } - } - - private void gotoButtonActionPerformed(ActionEvent evt) { - gotoUsage(); - setVisible(false); - } - - private void cancelButtonActionPerformed(ActionEvent evt) { - setVisible(false); - } - - @Override - public void mouseClicked(MouseEvent e) { - if (e.getClickCount() == 2) { - gotoUsage(); - } - } - - @Override - public void mousePressed(MouseEvent e) { - } - - @Override - public void mouseReleased(MouseEvent e) { - } - - @Override - public void mouseEntered(MouseEvent e) { - } - - @Override - public void mouseExited(MouseEvent e) { - } -} +/* + * Copyright (C) 2010-2015 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui.abc; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.usages.InsideClassMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MethodMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.TraitMultinameUsage; +import com.jpexs.decompiler.flash.gui.AppDialog; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.List; +import javax.swing.JButton; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +/** + * + * @author JPEXS + */ +public class UsageFrame extends AppDialog implements MouseListener { + + private final JButton gotoButton = new JButton(translate("button.goto")); + + private final JButton cancelButton = new JButton(translate("button.cancel")); + + private final JList usageList; + + private final UsageListModel usageListModel; + + private final ABCPanel abcPanel; + + public UsageFrame(ABC abc, int multinameIndex, ABCPanel abcPanel, boolean definitions) { + super(abcPanel.getMainPanel().getMainFrame().getWindow()); + this.abcPanel = abcPanel; + List usages = definitions ? abc.findMultinameDefinition(multinameIndex) : abc.findMultinameUsage(multinameIndex); + Multiname m = abc.constants.constant_multiname.get(multinameIndex); + if (m.namespace_index > 0 && abc.constants.constant_namespace.get(m.namespace_index).kind != Namespace.KIND_PRIVATE) { + for (ABCContainerTag at : abc.getAbcTags()) { + ABC a = at.getABC(); + if (a == abc) { + continue; + } + int mid = a.constants.getMultinameId(m, false); + if (mid > 0) { + usages.addAll(definitions ? a.findMultinameDefinition(mid) : a.findMultinameUsage(mid)); + } + } + } + usageListModel = new UsageListModel(); + for (MultinameUsage u : usages) { + usageListModel.addElement(u); + } + usageList = new JList<>(usageListModel); + usageList.setBackground(Color.white); + gotoButton.addActionListener(this::gotoButtonActionPerformed); + cancelButton.addActionListener(this::cancelButtonActionPerformed); + + JPanel buttonsPanel = new JPanel(); + buttonsPanel.setLayout(new FlowLayout()); + buttonsPanel.add(gotoButton); + buttonsPanel.add(cancelButton); + + usageList.addMouseListener(this); + Container cont = getContentPane(); + cont.setLayout(new BorderLayout()); + cont.add(new JScrollPane(usageList), BorderLayout.CENTER); + cont.add(buttonsPanel, BorderLayout.SOUTH); + setSize(400, 300); + setTitle((definitions ? translate("dialog.title.declaration") : translate("dialog.title")) + abc.constants.getMultiname(multinameIndex).getNameWithNamespace(abc.constants).toPrintableString()); + View.centerScreen(this); + View.setWindowIcon(this); + } + + public static void gotoUsage(final ABCPanel abcPanel, final MultinameUsage usage) { + if (usage instanceof InsideClassMultinameUsage) { + final InsideClassMultinameUsage icu = (InsideClassMultinameUsage) usage; + + Runnable settrait = new Runnable() { + + @Override + public void run() { + abcPanel.decompiledTextArea.removeScriptListener(this); + abcPanel.decompiledTextArea.setClassIndex(icu.classIndex); + if (usage instanceof TraitMultinameUsage) { + TraitMultinameUsage tmu = (TraitMultinameUsage) usage; + int traitIndex; + if (tmu.parentTraitIndex > -1) { + traitIndex = tmu.parentTraitIndex; + } else { + traitIndex = tmu.traitIndex; + } + if (!tmu.isStatic) { + traitIndex += abcPanel.abc.class_info.get(tmu.classIndex).static_traits.traits.size(); + } + if (tmu instanceof MethodMultinameUsage) { + MethodMultinameUsage mmu = (MethodMultinameUsage) usage; + if (mmu.isInitializer == true) { + traitIndex = abcPanel.abc.class_info.get(mmu.classIndex).static_traits.traits.size() + abcPanel.abc.instance_info.get(mmu.classIndex).instance_traits.traits.size() + (mmu.isStatic ? 1 : 0); + } + } + abcPanel.decompiledTextArea.gotoTrait(traitIndex); + } + } + }; + + if (abcPanel.decompiledTextArea.getClassIndex() == icu.classIndex && abcPanel.abc == icu.abc) { + settrait.run(); + } else { + abcPanel.decompiledTextArea.addScriptListener(settrait); + abcPanel.hilightScript(abcPanel.getSwf(), abcPanel.abc.instance_info.get(icu.classIndex).getName(abcPanel.abc.constants).getNameWithNamespace(abcPanel.abc.constants).toPrintableString()); + } + } + } + + private void gotoUsage() { + if (usageList.getSelectedIndex() != -1) { + MultinameUsage usage = usageListModel.getUsage(usageList.getSelectedIndex()); + gotoUsage(abcPanel, usage); + } + } + + private void gotoButtonActionPerformed(ActionEvent evt) { + gotoUsage(); + setVisible(false); + } + + private void cancelButtonActionPerformed(ActionEvent evt) { + setVisible(false); + } + + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + gotoUsage(); + } + } + + @Override + public void mousePressed(MouseEvent e) { + } + + @Override + public void mouseReleased(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java b/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java index d519d6808..6a6abcf3f 100644 --- a/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java +++ b/src/com/jpexs/decompiler/flash/gui/debugger/DebuggerTools.java @@ -93,7 +93,7 @@ public class DebuggerTools { ABC a = ct.getABC(); for (int i = 1; i < a.constants.constant_multiname.size(); i++) { Multiname m = a.constants.constant_multiname.get(i); - if ("trace".equals(m.getNameWithNamespace(a.constants, true))) { + if ("trace".equals(m.getNameWithNamespace(a.constants).toString())) { m.namespace_index = a.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, a.constants.getStringId(debuggerPkg, true)), 0, true); m.name_index = a.constants.getStringId(fname, true); ((Tag) ct).setModified(true); @@ -116,7 +116,7 @@ public class DebuggerTools { ABC a = ct.getABC(); for (int i = 1; i < a.constants.constant_multiname.size(); i++) { Multiname m = a.constants.constant_multiname.get(i); - String packageStr = m.getNameWithNamespace(a.constants, true); + String packageStr = m.getNameWithNamespace(a.constants).toString(); if (isDebuggerClass(packageStr, "debugTrace") || isDebuggerClass(packageStr, "debugAlert") || isDebuggerClass(packageStr, "debugSocket")