diff --git a/CHANGELOG.md b/CHANGELOG.md index a375f70ce..2eb13ab7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ All notable changes to this project will be documented in this file. - Set class to character mapping tool (via SymbolClass) context menu on characters - [#2189] Search bar in replace character (+ replace references) window - [#2011], [#2215] Option to ignore frame background color when exporting (make transparent) +- ABC Explorer - list of usages of all items +- ABC Explorer - items with zero usages are semi-transparent +- ABC Explorer - copy path to clipboard +- ABC Explorer - Go to path via `Ctrl + G` ### Fixed - Debugger - getting children of top level variables 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 99bd1192c..5c9350947 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 @@ -46,19 +46,10 @@ import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; import com.jpexs.decompiler.flash.abc.types.traits.TraitType; import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.decompiler.flash.abc.usages.multinames.ClassNameMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.multinames.ConstVarNameMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.multinames.ConstVarTypeMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MultinameUsageDetector; +import com.jpexs.decompiler.flash.abc.usages.Usage; import com.jpexs.decompiler.flash.abc.usages.multinames.DefinitionUsage; -import com.jpexs.decompiler.flash.abc.usages.multinames.MethodBodyMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.multinames.MethodNameMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.multinames.MethodParamsMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.multinames.MethodReturnTypeMultinameUsage; import com.jpexs.decompiler.flash.abc.usages.multinames.MultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.multinames.SuperClassMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.multinames.SuperInterfaceMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.multinames.TraitMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.multinames.TypeNameMultinameUsage; import com.jpexs.decompiler.flash.dumpview.DumpInfo; import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecial; import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecialType; @@ -1170,184 +1161,8 @@ public class ABC implements Openable { for (int i = 0; i < bodies.size(); i++) { output.println("MethodBody[" + i + "]:"); //+ bodies[i].toString(this, constants, method_info)); } - } - - private void checkMultinameUsedInMethod(int multinameIndex, boolean exactMatch, int methodInfo, List ret, int scriptIndex, int classIndex, int traitIndex, int traitsType, boolean isInitializer, Traits traits, int parentTraitIndex) { - for (int p = 0; p < method_info.get(methodInfo).param_types.length; p++) { - if (isSameName(multinameIndex, method_info.get(methodInfo).param_types[p], exactMatch)) { - ret.add(new MethodParamsMultinameUsage(this, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); - break; - } - } - if (isSameName(multinameIndex, method_info.get(methodInfo).ret_type, exactMatch)) { - ret.add(new MethodReturnTypeMultinameUsage(this, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); - } - MethodBody body = findBody(methodInfo); - if (body != null) { - findMultinameUsageInTraits(body.traits, multinameIndex, exactMatch, traitsType, scriptIndex, classIndex, ret, traitIndex); - for (ABCException e : body.exceptions) { - if ((isSameName(multinameIndex, e.name_index, exactMatch)) || (isSameName(multinameIndex, e.type_index, exactMatch))) { - ret.add(new MethodBodyMultinameUsage(this, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); - return; - } - } - for (AVM2Instruction ins : body.getCode().code) { - for (int o = 0; o < ins.definition.operands.length; o++) { - if (ins.definition.operands[o] == AVM2Code.DAT_MULTINAME_INDEX && ins.operands[o] < constants.getMultinameCount()) { - if (isSameName(multinameIndex, ins.operands[o], exactMatch)) { - ret.add(new MethodBodyMultinameUsage(this, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); - return; - } - } - } - } - } - } - - private void checkAllMultinameUsedInMethod(int methodInfo, List> ret, int scriptIndex, int classIndex, int traitIndex, int traitsType, boolean isInitializer, Traits traits, int parentTraitIndex) { - boolean[] foundMultinames = new boolean[constants.getMultinameCount()]; - for (int p = 0; p < method_info.get(methodInfo).param_types.length; p++) { - int methodParamsMultinameIndex = method_info.get(methodInfo).param_types[p]; - if (!foundMultinames[methodParamsMultinameIndex]) { - ret.get(methodParamsMultinameIndex).add(new MethodParamsMultinameUsage(this, methodParamsMultinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); - foundMultinames[methodParamsMultinameIndex] = true; - } - } - int methodReturnTypeMultinameIndex = method_info.get(methodInfo).ret_type; - ret.get(methodReturnTypeMultinameIndex).add(new MethodReturnTypeMultinameUsage(this, methodReturnTypeMultinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); - - MethodBody body = findBody(methodInfo); - if (body != null) { - findAllMultinameUsageInTraits(body.traits, traitsType, scriptIndex, classIndex, ret, traitIndex); - foundMultinames = new boolean[constants.getMultinameCount()]; - for (ABCException e : body.exceptions) { - if (!foundMultinames[e.name_index]) { - ret.get(e.name_index).add(new MethodBodyMultinameUsage(this, e.name_index, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); - foundMultinames[e.name_index] = true; - } - - if (!foundMultinames[e.type_index]) { - ret.get(e.type_index).add(new MethodBodyMultinameUsage(this, e.type_index, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); - foundMultinames[e.type_index] = true; - } - } - for (AVM2Instruction ins : body.getCode().code) { - for (int o = 0; o < ins.definition.operands.length; o++) { - if (ins.definition.operands[o] == AVM2Code.DAT_MULTINAME_INDEX) { - int mi = ins.operands[o]; - if (mi < foundMultinames.length && !foundMultinames[mi]) { - ret.get(mi).add(new MethodBodyMultinameUsage(this, mi, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); - foundMultinames[mi] = true; - } - } - } - } - } - } - - private boolean isSameName(int expectedQNameIndex, int checkedNameIndex, boolean exactMatch) { - if (expectedQNameIndex == checkedNameIndex) { - return true; - } - if (exactMatch) { - return false; - } - Multiname expectedQName = constants.getMultiname(expectedQNameIndex); - Multiname checkedName = constants.getMultiname(checkedNameIndex); - if (checkedName == null) { - return false; - } - - if (expectedQName.name_index != checkedName.name_index) { - return false; - } - if (checkedName.kind == Multiname.QNAME) { - return expectedQName.namespace_index == checkedName.namespace_index; - } - if (checkedName.kind != Multiname.MULTINAME) { - return false; - } - for (int ns : constants.getNamespaceSet(checkedName.namespace_set_index).namespaces) { - if (ns == expectedQName.namespace_index) { - return true; - } - } - return false; - } - - private void findMultinameUsageInTraits(Traits traits, int multinameIndex, boolean exactMatch, int traitsType, int scriptIndex, int classIndex, List ret, int parentTraitIndex) { - for (int t = 0; t < traits.traits.size(); t++) { - if (traits.traits.get(t) instanceof TraitClass) { - TraitClass tc = (TraitClass) traits.traits.get(t); - if (isSameName(multinameIndex, tc.name_index, exactMatch)) { - ret.add(new ClassNameMultinameUsage(this, multinameIndex, tc.class_info, scriptIndex)); - } - int c = tc.class_info; - if (isSameName(multinameIndex, instance_info.get(c).super_index, exactMatch)) { - ret.add(new SuperClassMultinameUsage(this, multinameIndex, c, scriptIndex)); - } - for (int i = 0; i < instance_info.get(c).interfaces.length; i++) { - if (isSameName(multinameIndex, instance_info.get(c).interfaces[i], exactMatch)) { - ret.add(new SuperInterfaceMultinameUsage(this, multinameIndex, c, scriptIndex)); - } - } - checkMultinameUsedInMethod(multinameIndex, exactMatch, instance_info.get(c).iinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, true, null, -1); - checkMultinameUsedInMethod(multinameIndex, exactMatch, class_info.get(c).cinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_CLASS, true, null, -1); - findMultinameUsageInTraits(instance_info.get(c).instance_traits, multinameIndex, exactMatch, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, -1/*FIXME*/, c, ret, -1); - findMultinameUsageInTraits(class_info.get(c).static_traits, multinameIndex, exactMatch, TraitMultinameUsage.TRAITS_TYPE_CLASS, -1/*FIXME*/, c, ret, -1); - } - if (traits.traits.get(t) instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) traits.traits.get(t); - if (isSameName(multinameIndex, tsc.name_index, exactMatch)) { - ret.add(new ConstVarNameMultinameUsage(this, multinameIndex, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex)); - } - if (isSameName(multinameIndex, tsc.type_index, exactMatch)) { - ret.add(new ConstVarTypeMultinameUsage(this, multinameIndex, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex)); - } - } - if (traits.traits.get(t) instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) traits.traits.get(t); - if (isSameName(multinameIndex, tmgs.name_index, exactMatch)) { - ret.add(new MethodNameMultinameUsage(this, multinameIndex, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex)); - } - checkMultinameUsedInMethod(multinameIndex, exactMatch, tmgs.method_info, ret, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex); - } - } - } - - private void findAllMultinameUsageInTraits(Traits traits, int traitsType, int scriptIndex, int classIndex, List> ret, int parentTraitIndex) { - for (int t = 0; t < traits.traits.size(); t++) { - if (traits.traits.get(t) instanceof TraitClass) { - TraitClass tc = (TraitClass) traits.traits.get(t); - ret.get(tc.name_index).add(new ClassNameMultinameUsage(this, tc.name_index, tc.class_info, scriptIndex)); - - int c = tc.class_info; - - int classNameMultinameIndex = instance_info.get(c).name_index; - ret.get(classNameMultinameIndex).add(new ClassNameMultinameUsage(this, classNameMultinameIndex, c, scriptIndex)); - int extendsMultinameIndex = instance_info.get(c).super_index; - ret.get(extendsMultinameIndex).add(new SuperClassMultinameUsage(this, extendsMultinameIndex, c, scriptIndex)); - for (int i = 0; i < instance_info.get(c).interfaces.length; i++) { - int implementsMultinameIndex = instance_info.get(c).interfaces[i]; - ret.get(implementsMultinameIndex).add(new SuperInterfaceMultinameUsage(this, implementsMultinameIndex, c, scriptIndex)); - } - checkAllMultinameUsedInMethod(instance_info.get(c).iinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, true, null, -1); - checkAllMultinameUsedInMethod(class_info.get(c).cinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_CLASS, true, null, -1); - findAllMultinameUsageInTraits(instance_info.get(c).instance_traits, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, -1/*FIXME*/, c, ret, -1); - findAllMultinameUsageInTraits(class_info.get(c).static_traits, TraitMultinameUsage.TRAITS_TYPE_CLASS, -1/*FIXME*/, c, ret, -1); - } - if (traits.traits.get(t) instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) traits.traits.get(t); - ret.get(tsc.name_index).add(new ConstVarNameMultinameUsage(this, tsc.name_index, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex)); - ret.get(tsc.type_index).add(new ConstVarTypeMultinameUsage(this, tsc.type_index, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex)); - } - if (traits.traits.get(t) instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) traits.traits.get(t); - ret.get(tmgs.name_index).add(new MethodNameMultinameUsage(this, tmgs.name_index, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex)); - checkAllMultinameUsedInMethod(tmgs.method_info, ret, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex); - } - } - } + } + public List findMultinameDefinition(int multinameIndex) { List usages = findMultinameUsage(multinameIndex, false); @@ -1369,6 +1184,16 @@ public class ABC implements Openable { } return ret; } + + public List findMultinameUsage(int multinameIndex, boolean exactMatch) { + MultinameUsageDetector det = new MultinameUsageDetector(); + List retUsage = det.findMultinameUsage(this, multinameIndex, exactMatch); + List ret = new ArrayList<>(); + for (Usage u : retUsage) { + ret.add((MultinameUsage) u); + } + return ret; + } /** * Gets colliding usages of multinames. For example same name @@ -1397,20 +1222,23 @@ public class ABC implements Openable { } Set collidingUsages = new HashSet<>(); + MultinameUsageDetector det = new MultinameUsageDetector(); // find context of names with count 2 or more - List> usagesList = findAllMultinameUsage(); + List> usagesList = det.findAllUsage(this); for (String name : nameToQNameIndices.keySet()) { List multinameIndices = nameToQNameIndices.get(name); if (multinameIndices.size() > 1) { - List> allUsages = new ArrayList<>(); + List> allUsages = new ArrayList<>(); for (int multinameIndex : multinameIndices) { - List usages = usagesList.get(multinameIndex); - for (MultinameUsage usage : usages) { - for (List prevUsages : allUsages) { - for (MultinameUsage prevUsage : prevUsages) { - if (prevUsage.collides(usage)) { - collidingUsages.add(usage); - collidingUsages.add(prevUsage); + List usages = usagesList.get(multinameIndex); + for (Usage usage : usages) { + for (List prevUsages : allUsages) { + for (Usage prevUsage : prevUsages) { + MultinameUsage usageM = (MultinameUsage) usage; + MultinameUsage prevUsageM = (MultinameUsage) prevUsage; + if (prevUsageM.collides(usageM)) { + collidingUsages.add(usageM); + collidingUsages.add(prevUsageM); } } } @@ -1442,63 +1270,7 @@ public class ABC implements Openable { } } - public List findMultinameUsage(int multinameIndex, boolean exactMatch) { - List ret = new ArrayList<>(); - if (multinameIndex == 0) { - return ret; - } - for (int s = 0; s < script_info.size(); s++) { - checkMultinameUsedInMethod(multinameIndex, exactMatch, script_info.get(s).init_index, ret, s, -1, 0, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, true, null, -1); - findMultinameUsageInTraits(script_info.get(s).traits, multinameIndex, exactMatch, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, s, -1, ret, -1); - } - loopm: - for (int t = 1; t < constants.getMultinameCount(); t++) { - Multiname multiname = constants.getMultiname(t); - if (multiname.kind == Multiname.TYPENAME) { - if (multiname.qname_index == multinameIndex) { - ret.add(new TypeNameMultinameUsage(this, multinameIndex, t, -1)); - continue; - } - for (int mp : multiname.params) { - if (mp == multinameIndex) { - ret.add(new TypeNameMultinameUsage(this, multinameIndex, t, -1)); - continue loopm; - } - } - } - } - return ret; - } - - public List> findAllMultinameUsage() { - List> ret = new ArrayList<>(); - for (int i = 0; i < constants.getMultinameCount(); i++) { - ret.add(new ArrayList<>()); - } - - for (int s = 0; s < script_info.size(); s++) { - checkAllMultinameUsedInMethod(script_info.get(s).init_index, ret, s, -1, 0, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, true, null, -1); - findAllMultinameUsageInTraits(script_info.get(s).traits, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, s, -1, ret, -1); - } - - boolean[] foundMultinames = new boolean[constants.getMultinameCount()]; - for (int t = 1; t < constants.getMultinameCount(); t++) { - Multiname multiname = constants.getMultiname(t); - if (multiname.kind == Multiname.TYPENAME) { - if (!foundMultinames[multiname.qname_index]) { - ret.get(multiname.qname_index).add(new TypeNameMultinameUsage(this, multiname.qname_index, t, -1)); - foundMultinames[multiname.qname_index] = true; - } - for (int mp : multiname.params) { - if (!foundMultinames[mp]) { - ret.get(mp).add(new TypeNameMultinameUsage(this, mp, t, -1)); - foundMultinames[mp] = true; - } - } - } - } - return ret; - } + public int findMethodInfoByName(int classId, String methodNameWithSuffix) { if (classId > -1) { @@ -2503,5 +2275,5 @@ public class ABC implements Openable { public long getDataSize() { return dataSize; - } + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java index d3b04d597..d4fded805 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2ConstantPool.java @@ -36,6 +36,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.logging.Logger; /** @@ -870,5 +871,5 @@ public class AVM2ConstantPool implements Cloneable { for (int i = 0; i < constant_multiname.size(); i++) { Multiname.checkTypeNameCyclic(this, i); } - } + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/MultinameUsageDetector.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/MultinameUsageDetector.java new file mode 100644 index 000000000..f087d492f --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/MultinameUsageDetector.java @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2010-2024 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.usages; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.abc.usages.multinames.ClassNameMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.multinames.ConstVarNameMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.multinames.ConstVarTypeMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.multinames.MethodBodyMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.multinames.MethodNameMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.multinames.MethodParamsMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.multinames.MethodReturnTypeMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.multinames.SuperClassMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.multinames.SuperInterfaceMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.multinames.TraitMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.multinames.TypeNameMultinameUsage; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class MultinameUsageDetector implements UsageDetector { + + @Override + public List findUsages(ABC abc, int index) { + return findMultinameUsage(abc, index, true); + } + + public List findMultinameUsage(ABC abc, int multinameIndex, boolean exactMatch) { + List ret = new ArrayList<>(); + if (multinameIndex == 0) { + return ret; + } + for (int s = 0; s < abc.script_info.size(); s++) { + checkMultinameUsedInMethod(abc, multinameIndex, exactMatch, abc.script_info.get(s).init_index, ret, s, -1, 0, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, true, null, -1); + findMultinameUsageInTraits(abc, abc.script_info.get(s).traits, multinameIndex, exactMatch, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, s, -1, ret, -1); + } + loopm: + for (int t = 1; t < abc.constants.getMultinameCount(); t++) { + Multiname multiname = abc.constants.getMultiname(t); + if (multiname.kind == Multiname.TYPENAME) { + if (multiname.qname_index == multinameIndex) { + ret.add(new TypeNameMultinameUsage(abc, multinameIndex, t, -1)); + continue; + } + for (int mp : multiname.params) { + if (mp == multinameIndex) { + ret.add(new TypeNameMultinameUsage(abc, multinameIndex, t, -1)); + continue loopm; + } + } + } + } + return ret; + } + + @Override + public List> findAllUsage(ABC abc) { + List> ret = new ArrayList<>(); + for (int i = 0; i < abc.constants.getMultinameCount(); i++) { + ret.add(new ArrayList<>()); + } + + for (int s = 0; s < abc.script_info.size(); s++) { + checkAllMultinameUsedInMethod(abc, abc.script_info.get(s).init_index, ret, s, -1, 0, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, true, null, -1); + findAllMultinameUsageInTraits(abc, abc.script_info.get(s).traits, TraitMultinameUsage.TRAITS_TYPE_SCRIPT, s, -1, ret, -1); + } + + boolean[] foundMultinames = new boolean[abc.constants.getMultinameCount()]; + for (int t = 1; t < abc.constants.getMultinameCount(); t++) { + Multiname multiname = abc.constants.getMultiname(t); + if (multiname.kind == Multiname.TYPENAME) { + if (!foundMultinames[multiname.qname_index]) { + ret.get(multiname.qname_index).add(new TypeNameMultinameUsage(abc, multiname.qname_index, t, -1)); + foundMultinames[multiname.qname_index] = true; + } + for (int mp : multiname.params) { + if (!foundMultinames[mp]) { + ret.get(mp).add(new TypeNameMultinameUsage(abc, mp, t, -1)); + foundMultinames[mp] = true; + } + } + } + } + return ret; + } + + @Override + public String getKind() { + return "string"; + } + + private void findAllMultinameUsageInTraits(ABC abc, Traits traits, int traitsType, int scriptIndex, int classIndex, List> ret, int parentTraitIndex) { + for (int t = 0; t < traits.traits.size(); t++) { + if (traits.traits.get(t) instanceof TraitClass) { + TraitClass tc = (TraitClass) traits.traits.get(t); + ret.get(tc.name_index).add(new ClassNameMultinameUsage(abc, tc.name_index, tc.class_info, scriptIndex)); + + int c = tc.class_info; + + int classNameMultinameIndex = abc.instance_info.get(c).name_index; + ret.get(classNameMultinameIndex).add(new ClassNameMultinameUsage(abc, classNameMultinameIndex, c, scriptIndex)); + int extendsMultinameIndex = abc.instance_info.get(c).super_index; + ret.get(extendsMultinameIndex).add(new SuperClassMultinameUsage(abc, extendsMultinameIndex, c, scriptIndex)); + for (int i = 0; i < abc.instance_info.get(c).interfaces.length; i++) { + int implementsMultinameIndex = abc.instance_info.get(c).interfaces[i]; + ret.get(implementsMultinameIndex).add(new SuperInterfaceMultinameUsage(abc, implementsMultinameIndex, c, scriptIndex)); + } + checkAllMultinameUsedInMethod(abc, abc.instance_info.get(c).iinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, true, null, -1); + checkAllMultinameUsedInMethod(abc, abc.class_info.get(c).cinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_CLASS, true, null, -1); + findAllMultinameUsageInTraits(abc, abc.instance_info.get(c).instance_traits, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, -1/*FIXME*/, c, ret, -1); + findAllMultinameUsageInTraits(abc, abc.class_info.get(c).static_traits, TraitMultinameUsage.TRAITS_TYPE_CLASS, -1/*FIXME*/, c, ret, -1); + } + if (traits.traits.get(t) instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) traits.traits.get(t); + ret.get(tsc.name_index).add(new ConstVarNameMultinameUsage(abc, tsc.name_index, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex)); + ret.get(tsc.type_index).add(new ConstVarTypeMultinameUsage(abc, tsc.type_index, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex)); + } + if (traits.traits.get(t) instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) traits.traits.get(t); + ret.get(tmgs.name_index).add(new MethodNameMultinameUsage(abc, tmgs.name_index, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex)); + checkAllMultinameUsedInMethod(abc, tmgs.method_info, ret, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex); + } + } + } + + private void checkAllMultinameUsedInMethod(ABC abc, int methodInfo, List> ret, int scriptIndex, int classIndex, int traitIndex, int traitsType, boolean isInitializer, Traits traits, int parentTraitIndex) { + boolean[] foundMultinames = new boolean[abc.constants.getMultinameCount()]; + for (int p = 0; p < abc.method_info.get(methodInfo).param_types.length; p++) { + int methodParamsMultinameIndex = abc.method_info.get(methodInfo).param_types[p]; + if (!foundMultinames[methodParamsMultinameIndex]) { + ret.get(methodParamsMultinameIndex).add(new MethodParamsMultinameUsage(abc, methodParamsMultinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); + foundMultinames[methodParamsMultinameIndex] = true; + } + } + int methodReturnTypeMultinameIndex = abc.method_info.get(methodInfo).ret_type; + ret.get(methodReturnTypeMultinameIndex).add(new MethodReturnTypeMultinameUsage(abc, methodReturnTypeMultinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); + + MethodBody body = abc.findBody(methodInfo); + if (body != null) { + findAllMultinameUsageInTraits(abc, body.traits, traitsType, scriptIndex, classIndex, ret, traitIndex); + foundMultinames = new boolean[abc.constants.getMultinameCount()]; + for (ABCException e : body.exceptions) { + if (!foundMultinames[e.name_index]) { + ret.get(e.name_index).add(new MethodBodyMultinameUsage(abc, e.name_index, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); + foundMultinames[e.name_index] = true; + } + + if (!foundMultinames[e.type_index]) { + ret.get(e.type_index).add(new MethodBodyMultinameUsage(abc, e.type_index, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); + foundMultinames[e.type_index] = true; + } + } + for (AVM2Instruction ins : body.getCode().code) { + for (int o = 0; o < ins.definition.operands.length; o++) { + if (ins.definition.operands[o] == AVM2Code.DAT_MULTINAME_INDEX) { + int mi = ins.operands[o]; + if (mi < foundMultinames.length && !foundMultinames[mi]) { + ret.get(mi).add(new MethodBodyMultinameUsage(abc, mi, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); + foundMultinames[mi] = true; + } + } + } + } + } + } + + private boolean isSameName(ABC abc, int expectedQNameIndex, int checkedNameIndex, boolean exactMatch) { + if (expectedQNameIndex == checkedNameIndex) { + return true; + } + if (exactMatch) { + return false; + } + Multiname expectedQName = abc.constants.getMultiname(expectedQNameIndex); + Multiname checkedName = abc.constants.getMultiname(checkedNameIndex); + if (checkedName == null) { + return false; + } + + if (expectedQName.name_index != checkedName.name_index) { + return false; + } + if (checkedName.kind == Multiname.QNAME) { + return expectedQName.namespace_index == checkedName.namespace_index; + } + if (checkedName.kind != Multiname.MULTINAME) { + return false; + } + for (int ns : abc.constants.getNamespaceSet(checkedName.namespace_set_index).namespaces) { + if (ns == expectedQName.namespace_index) { + return true; + } + } + return false; + } + + + private void findMultinameUsageInTraits(ABC abc, Traits traits, int multinameIndex, boolean exactMatch, int traitsType, int scriptIndex, int classIndex, List ret, int parentTraitIndex) { + for (int t = 0; t < traits.traits.size(); t++) { + if (traits.traits.get(t) instanceof TraitClass) { + TraitClass tc = (TraitClass) traits.traits.get(t); + if (isSameName(abc, multinameIndex, tc.name_index, exactMatch)) { + ret.add(new ClassNameMultinameUsage(abc, multinameIndex, tc.class_info, scriptIndex)); + } + int c = tc.class_info; + if (isSameName(abc, multinameIndex, abc.instance_info.get(c).super_index, exactMatch)) { + ret.add(new SuperClassMultinameUsage(abc, multinameIndex, c, scriptIndex)); + } + for (int i = 0; i < abc.instance_info.get(c).interfaces.length; i++) { + if (isSameName(abc, multinameIndex, abc.instance_info.get(c).interfaces[i], exactMatch)) { + ret.add(new SuperInterfaceMultinameUsage(abc, multinameIndex, c, scriptIndex)); + } + } + checkMultinameUsedInMethod(abc, multinameIndex, exactMatch, abc.instance_info.get(c).iinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, true, null, -1); + checkMultinameUsedInMethod(abc, multinameIndex, exactMatch, abc.class_info.get(c).cinit_index, ret, -1/*FIXME*/, c, 0, TraitMultinameUsage.TRAITS_TYPE_CLASS, true, null, -1); + findMultinameUsageInTraits(abc, abc.instance_info.get(c).instance_traits, multinameIndex, exactMatch, TraitMultinameUsage.TRAITS_TYPE_INSTANCE, -1/*FIXME*/, c, ret, -1); + findMultinameUsageInTraits(abc, abc.class_info.get(c).static_traits, multinameIndex, exactMatch, TraitMultinameUsage.TRAITS_TYPE_CLASS, -1/*FIXME*/, c, ret, -1); + } + if (traits.traits.get(t) instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) traits.traits.get(t); + if (isSameName(abc, multinameIndex, tsc.name_index, exactMatch)) { + ret.add(new ConstVarNameMultinameUsage(abc, multinameIndex, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex)); + } + if (isSameName(abc, multinameIndex, tsc.type_index, exactMatch)) { + ret.add(new ConstVarTypeMultinameUsage(abc, multinameIndex, scriptIndex, classIndex, t, traitsType, traits, parentTraitIndex)); + } + } + if (traits.traits.get(t) instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) traits.traits.get(t); + if (isSameName(abc, multinameIndex, tmgs.name_index, exactMatch)) { + ret.add(new MethodNameMultinameUsage(abc, multinameIndex, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex)); + } + checkMultinameUsedInMethod(abc, multinameIndex, exactMatch, tmgs.method_info, ret, scriptIndex, classIndex, t, traitsType, false, traits, parentTraitIndex); + } + } + } + + + private void checkMultinameUsedInMethod(ABC abc, int multinameIndex, boolean exactMatch, int methodInfo, List ret, int scriptIndex, int classIndex, int traitIndex, int traitsType, boolean isInitializer, Traits traits, int parentTraitIndex) { + for (int p = 0; p < abc.method_info.get(methodInfo).param_types.length; p++) { + if (isSameName(abc, multinameIndex, abc.method_info.get(methodInfo).param_types[p], exactMatch)) { + ret.add(new MethodParamsMultinameUsage(abc, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); + break; + } + } + if (isSameName(abc, multinameIndex, abc.method_info.get(methodInfo).ret_type, exactMatch)) { + ret.add(new MethodReturnTypeMultinameUsage(abc, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); + } + MethodBody body = abc.findBody(methodInfo); + if (body != null) { + findMultinameUsageInTraits(abc, body.traits, multinameIndex, exactMatch, traitsType, scriptIndex, classIndex, ret, traitIndex); + for (ABCException e : body.exceptions) { + if ((isSameName(abc, multinameIndex, e.name_index, exactMatch)) || (isSameName(abc, multinameIndex, e.type_index, exactMatch))) { + ret.add(new MethodBodyMultinameUsage(abc, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); + return; + } + } + for (AVM2Instruction ins : body.getCode().code) { + for (int o = 0; o < ins.definition.operands.length; o++) { + if (ins.definition.operands[o] == AVM2Code.DAT_MULTINAME_INDEX && ins.operands[o] < abc.constants.getMultinameCount()) { + if (isSameName(abc, multinameIndex, ins.operands[o], exactMatch)) { + ret.add(new MethodBodyMultinameUsage(abc, multinameIndex, scriptIndex, classIndex, traitIndex, traitsType, isInitializer, traits, parentTraitIndex)); + return; + } + } + } + } + } + } + + @Override + public void filterUsages(ABC abc, UsagesFilter filter) { + throw new UnsupportedOperationException("Not supported yet."); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/Usage.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/Usage.java new file mode 100644 index 000000000..0f24d1274 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/Usage.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010-2024 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.usages; + +import com.jpexs.decompiler.flash.abc.ABC; + +/** + * + * @author JPEXS + */ +public interface Usage { + public ABC getAbc(); + + public int getIndex(); + + public String getKind(); +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/UsageDetector.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/UsageDetector.java new file mode 100644 index 000000000..ac8bac0c8 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/UsageDetector.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010-2024 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.usages; + +import com.jpexs.decompiler.flash.abc.ABC; +import java.util.List; + +/** + * + * @author JPEXS + */ +public interface UsageDetector { + + public void filterUsages(ABC abc, UsagesFilter filter); + + public List findUsages(ABC abc, int index); + + public List> findAllUsage(ABC abc); + + public String getKind(); +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/UsagesFilter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/UsagesFilter.java new file mode 100644 index 000000000..2815c751d --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/UsagesFilter.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2010-2024 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.usages; + +/** + * + * @author JPEXS + */ +public interface UsagesFilter { + public int filterUsage(int originalIndex); +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/multinames/MultinameUsage.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/multinames/MultinameUsage.java index 28cab8e3f..d4b4f3a64 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/multinames/MultinameUsage.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/multinames/MultinameUsage.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.abc.usages.multinames; 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.Usage; import java.util.ArrayList; import java.util.Objects; @@ -26,7 +27,7 @@ import java.util.Objects; * * @author JPEXS */ -public abstract class MultinameUsage { +public abstract class MultinameUsage implements Usage { protected final ABC abc; @@ -45,6 +46,7 @@ public abstract class MultinameUsage { return multinameIndex; } + @Override public ABC getAbc() { return abc; } @@ -124,4 +126,14 @@ public abstract class MultinameUsage { } public abstract boolean collides(MultinameUsage other); + + @Override + public int getIndex() { + return multinameIndex; + } + + @Override + public String getKind() { + return "multiname"; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/simple/ABCSimpleUsageDetector.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/simple/ABCSimpleUsageDetector.java new file mode 100644 index 000000000..50cc52374 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/simple/ABCSimpleUsageDetector.java @@ -0,0 +1,448 @@ +/* + * Copyright (C) 2010-2023 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.usages.simple; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.types.InstanceInfo; +import com.jpexs.decompiler.flash.abc.types.MetadataInfo; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.flash.abc.types.ValueKind; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; +import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +/** + * + * @author JPEXS + */ +public class ABCSimpleUsageDetector { + + public static enum ItemKind { + INT, + UINT, + DOUBLE, + STRING, + NAMESPACE, + NAMESPACESET, + MULTINAME, + METADATAINFO, + METHODINFO, + METHODBODY, + CLASS + } + + private final Map>> usages = new HashMap<>(); + + private final ABC abc; + public ABCSimpleUsageDetector(ABC abc) { + this.abc = abc; + } + + private void initUsages(ItemKind kind, int itemCount, boolean atleastOne) { + List> list = new ArrayList<>(); + if (atleastOne && itemCount == 0) { + itemCount = 1; + } + for (int i = 0; i < itemCount; i++) { + list.add(new ArrayList<>()); + } + usages.put(kind, list); + } + + public void detect() { + usages.clear(); + + initUsages(ItemKind.INT, abc.constants.getIntCount(), true); + initUsages(ItemKind.UINT, abc.constants.getUIntCount(), true); + initUsages(ItemKind.DOUBLE, abc.constants.getDoubleCount(), true); + initUsages(ItemKind.STRING, abc.constants.getStringCount(), true); + initUsages(ItemKind.NAMESPACE, abc.constants.getNamespaceCount(), true); + initUsages(ItemKind.NAMESPACESET, abc.constants.getNamespaceSetCount(), true); + initUsages(ItemKind.MULTINAME, abc.constants.getMultinameCount(), true); + initUsages(ItemKind.METADATAINFO, abc.metadata_info.size(), false); + initUsages(ItemKind.METHODINFO, abc.method_info.size(), false); + initUsages(ItemKind.METHODBODY, abc.bodies.size(), false); + initUsages(ItemKind.CLASS, abc.class_info.size(), false); + + ABCWalker walker = new ABCWalker() { + protected void handleUsageNamespace(int index, String usageDescription) { + if (!handleUsage(ItemKind.NAMESPACE, index, usageDescription)) { + return; + } + Namespace ns = abc.constants.getNamespace(index); + if (ns == null) { + return; + } + handleUsage(ItemKind.STRING, ns.name_index, "ns" + index + "/name"); + } + + protected void handleUsageNamespaceSet(int index, String usageDescription) { + if (!handleUsage(ItemKind.NAMESPACESET, index, usageDescription)) { + return; + } + NamespaceSet nss = abc.constants.getNamespaceSet(index); + if (nss == null) { + return; + } + for (int i = 0; i < nss.namespaces.length; i++) { + handleUsageNamespace(nss.namespaces[i], "nss" + index + "/ns" + nss.namespaces[i]); + } + } + + protected void handleUsageMultiname(int index, String usageDescription) { + if (!handleUsage(ItemKind.MULTINAME, index, usageDescription)) { + return; + } + Multiname m = abc.constants.getMultiname(index); + if (m == null) { + return; + } + if (m.hasOwnName()) { + handleUsage(ItemKind.STRING, m.name_index, "mn" + index + "/name"); + } + if (m.hasOwnNamespace()) { + handleUsageNamespace(m.namespace_index, "mn" + index + "/namespace"); + } + if (m.hasOwnNamespaceSet()) { + handleUsageNamespaceSet(m.namespace_set_index, "mn" + index + "/namespace_set"); + } + if (m.kind == Multiname.TYPENAME) { + handleUsageMultiname(m.qname_index, "mn" + index + "/qname"); + for (int i = 0; i < m.params.length; i++) { + handleUsageMultiname(m.params[i], "mn" + index + "/param" + i); + } + } + } + + protected void handleUsageMethodInfo(int index, String usageDescription) { + if (!handleUsage(ItemKind.METHODINFO, index, usageDescription)) { + return; + } + + MethodInfo m = abc.method_info.get(index); + handleUsage(ItemKind.STRING, m.name_index, usageDescription + "/name"); + + if (m.flagHas_paramnames()) { + for (int i = 0; i < m.paramNames.length; i++) { + handleUsage(ItemKind.STRING, m.paramNames[i], usageDescription + "/param_names/pn" + i); + } + } + if (m.flagHas_optional()) { + for (int i = 0; i < m.optional.length; i++) { + handleUsageValueKind(m.optional[i].value_kind, m.optional[i].value_index, usageDescription + "/optional/op" + i); + } + } + + for (int i = 0; i < m.param_types.length; i++) { + handleUsageMultiname(m.param_types[i], usageDescription + "/param_types/pt" + i); + } + + handleUsageMultiname(m.ret_type, usageDescription + "/return_type"); + + int bodyIndex = abc.findBodyIndex(index); + if (bodyIndex > -1) { + handleUsageMethodBody(bodyIndex, usageDescription + "/method_body"); + } + } + + protected void handleUsageMethodBody(int index, String usageDescription) { + if (!handleUsage(ItemKind.METHODBODY, index, usageDescription)) { + return; + } + MethodBody body = abc.bodies.get(index); + for (int i = 0; i < body.exceptions.length; i++) { + handleUsageMultiname(body.exceptions[i].name_index, usageDescription + "/exceptions/ex" + i + "/name"); + handleUsageMultiname(body.exceptions[i].type_index, usageDescription + "/exceptions/ex" + i + "/type"); + } + List code = body.getCode().code; + for (int i = 0; i < code.size(); i++) { + AVM2Instruction ins = code.get(i); + for (int operandIndex = 0; operandIndex < ins.definition.operands.length; operandIndex++) { + int operand = ins.operands[operandIndex]; + String operandDescription = usageDescription + "/code/ins" + i + "/op" + operandIndex; + switch (ins.definition.operands[operandIndex]) { + case AVM2Code.DAT_CLASS_INDEX: + break; + case AVM2Code.DAT_DOUBLE_INDEX: + handleUsage(ItemKind.DOUBLE, operand, operandDescription); + break; + case AVM2Code.DAT_INT_INDEX: + handleUsage(ItemKind.INT, operand, operandDescription); + break; + case AVM2Code.DAT_METHOD_INDEX: + //handleUsageMethodInfo(operand, operandDescription); + break; + case AVM2Code.DAT_MULTINAME_INDEX: + handleUsageMultiname(operand, operandDescription); + break; + case AVM2Code.DAT_NAMESPACE_INDEX: + handleUsageNamespace(operand, operandDescription); + break; + case AVM2Code.DAT_STRING_INDEX: + handleUsage(ItemKind.STRING, operand, operandDescription); + break; + case AVM2Code.DAT_UINT_INDEX: + handleUsage(ItemKind.UINT, operand, operandDescription); + break; + } + } + } + } + + @Override + protected void handleScript(ABC abc, int index) { + + } + + @Override + protected void handleMetadataInfo(ABC abc, int index, Trait trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, int traitMetadataIndex, ABCWalker.WalkType walkType) { + String description = "si" + scriptIndex + "/traits/t" + scriptTraitIndex; + if (walkType == WalkType.Class && traitIndex > -1) { + description += "/class_info/traits/t" + traitIndex; + } else if (walkType == WalkType.Instance && traitIndex > -1) { + description += "/instance_info/traits/t" + traitIndex; + } + description += "/metadata/md" + index; + + //?? classIndex + + if (!handleUsage(ItemKind.METADATAINFO, index, description)) { + return; + } + MetadataInfo md = abc.metadata_info.get(index); + handleUsage(ItemKind.STRING, md.name_index, description + "/name"); + for (int i = 0; i < md.keys.length; i++) { + handleUsage(ItemKind.STRING, md.keys[i], description + "/pairs/p" + i + "/key"); + } + for (int i = 0; i < md.values.length; i++) { + handleUsage(ItemKind.STRING, md.values[i], description + "/pairs/p" + i + "/value"); + } + } + + @Override + protected void handleTraitClass(ABC abc, TraitClass trait, int scriptIndex, int scriptTraitIndex) { + String description = "si" + scriptIndex + "/traits/t" + scriptTraitIndex; + if (!handleUsage(ItemKind.CLASS, trait.class_info, description)) { + return; + } + + InstanceInfo ii = abc.instance_info.get(trait.class_info); + if ((ii.flags & InstanceInfo.CLASS_PROTECTEDNS) != 0) { + handleUsageNamespace(ii.protectedNS, description + "/instance_info/protected_ns"); + } + handleUsageMultiname(ii.name_index, description + "/instance_info/name"); + handleUsageMultiname(ii.super_index, description + "/instance_info/super"); + for (int i = 0; i < ii.interfaces.length; i++) { + handleUsageMultiname(ii.interfaces[i], description + "/instance_info/interfaces/in" + i); + } + /* + handleUsageMethodInfo(abc.class_info.get(trait.class_info).cinit_index, description + "/class_info/cinit"); + handleUsageMethodInfo(abc.instance_info.get(trait.class_info).iinit_index, description + "/instance_info/iinit"); + */ + } + + @Override + protected void handleTraitMethodGetterSetter(ABC abc, TraitMethodGetterSetter trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, ABCWalker.WalkType walkType) { + handleTraitMethodBase(abc, trait, scriptIndex, scriptTraitIndex, classIndex, traitIndex, walkType); + } + + + protected void handleTraitMethodBase(ABC abc, Trait trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, ABCWalker.WalkType walkType) { + String description = ""; + if (scriptIndex > -1) { + description += "si" + scriptIndex + "/traits/t" + scriptTraitIndex; + if (walkType == WalkType.Class && traitIndex > -1) { + description += "/class_info/traits/t" + traitIndex; + } else if (walkType == WalkType.Instance && traitIndex > -1) { + description += "/instance_info/traits/t" + traitIndex; + } + } else { + if (walkType == WalkType.Class && traitIndex > -1) { + description += "ci" + classIndex + "/class_info/traits/t" + traitIndex; + } else if (walkType == WalkType.Instance && traitIndex > -1) { + description += "ii" + classIndex + "/instance_info/traits/t" + traitIndex; + } + } + + //description += " " + trait.getKindToStr(); + + handleUsageMultiname(trait.name_index, description + "/name"); + //handleUsageMethodInfo(trait.method_info, description + "/method_info"); + } + + @Override + protected void handleTraitFunction(ABC abc, TraitFunction trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, ABCWalker.WalkType walkType) { + handleTraitMethodBase(abc, trait, scriptIndex, scriptTraitIndex, classIndex, traitIndex, walkType); + } + + protected void handleUsageValueKind(int value_kind, int value_index, String description) { + switch (value_kind) { + case ValueKind.CONSTANT_Int: + handleUsage(ItemKind.INT, value_index, description); + break; + case ValueKind.CONSTANT_UInt: + handleUsage(ItemKind.UINT, value_index, description); + break; + case ValueKind.CONSTANT_Double: + handleUsage(ItemKind.DOUBLE, value_index, description); + break; + case ValueKind.CONSTANT_Utf8: + handleUsage(ItemKind.STRING, value_index, description); + break; + case ValueKind.CONSTANT_Namespace: + case ValueKind.CONSTANT_PackageNamespace: + case ValueKind.CONSTANT_PackageInternalNs: + case ValueKind.CONSTANT_ProtectedNamespace: + case ValueKind.CONSTANT_ExplicitNamespace: + case ValueKind.CONSTANT_StaticProtectedNs: + case ValueKind.CONSTANT_PrivateNs: + handleUsage(ItemKind.NAMESPACE, value_index, description); + break; + } + } + + @Override + protected void handleTraitSlotConst(ABC abc, TraitSlotConst trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, int bodyIndex, int bodyTraitIndex, ABCWalker.WalkType walkType, Stack callStack) { + + String description = ""; + if (callStack.size() > 1) { + if (bodyTraitIndex != -1) { + int methodInfo = callStack.peek(); + description += "mi" + methodInfo + "/method_body/traits/t" + bodyTraitIndex; + } + } else if (scriptIndex > -1) { + description += "si" + scriptIndex + "/traits/t" + scriptTraitIndex; + + if (walkType == WalkType.Class && traitIndex > -1) { + description += "/class_info/traits/t" + traitIndex; + } else if (walkType == WalkType.Instance && traitIndex > -1) { + description += "/instance_info/traits/t" + traitIndex; + } + + if (bodyTraitIndex != -1) { + description += "/method_info/method_body/traits/t" + bodyTraitIndex; + } + } else { + if (walkType == WalkType.Class && traitIndex > -1) { + description += "ci" + classIndex + "/class_info/traits/t" + traitIndex; + } else if (walkType == WalkType.Instance && traitIndex > -1) { + description += "ii" + classIndex + "/instance_info/traits/t" + traitIndex; + } else if (bodyTraitIndex > -1) { + description += "mb" + bodyIndex + "/traits/t" + bodyTraitIndex; + } + } + + //description += " " + trait.getKindToStr(); + handleUsageMultiname(trait.name_index, description + "/name"); + handleUsageMultiname(trait.type_index, description + "/type"); + handleUsageValueKind(trait.value_kind, trait.value_index, description + "/value_index"); + } + + @Override + protected void handleMethodInfo(ABC abc, int index, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, ABCWalker.WalkType walkType, boolean initializer, Stack callStack) { + if (callStack.size() > 1) { //it is an anonymous submethod + int prevMethod = callStack.get(callStack.size() - 2); + handleUsageMethodInfo(index, "mi" + prevMethod + "/method_body/code"); + return; + } + if (initializer) { + switch (walkType) { + case Class: + if (scriptIndex != -1) { + handleUsageMethodInfo(index, "si" + scriptIndex + "/traits/t" + scriptTraitIndex + "/class_info/cinit"); + } else { + handleUsageMethodInfo(index, "ci" + classIndex + "/class_info/cinit"); + } + break; + case Instance: + if (scriptIndex != -1) { + handleUsageMethodInfo(index, "si" + scriptIndex + "/traits/t" + scriptTraitIndex + "/instance_info/iinit"); + } else { + handleUsageMethodInfo(index, "ii" + classIndex + "/instance_info/iinit"); + } + break; + case Script: + handleUsageMethodInfo(index, "si" + scriptIndex + "/init"); + break; + + } + return; + } + + String description = ""; + if (scriptIndex > -1) { + description += "si" + scriptIndex + "/traits/t" + scriptTraitIndex; + if (walkType == WalkType.Class && traitIndex > -1) { + description += "/class_info/traits/t" + traitIndex; + } else if (walkType == WalkType.Instance && traitIndex > -1) { + description += "/instance_info/traits/t" + traitIndex; + } + } else { + if (walkType == WalkType.Class && traitIndex > -1) { + description += "ci" + classIndex + "/class_info/traits/t" + traitIndex; + } else if (walkType == WalkType.Instance && traitIndex > -1) { + description += "ii" + classIndex + "/instance_info/traits/t" + traitIndex; + } + } + description += "/method_info"; + handleUsageMethodInfo(index, description); + } + }; + walker.walkABC(abc, false); + } + + /** + * + * @param kind + * @param index + * @param usageDescription + * @return True if it is new + */ + private boolean handleUsage(ItemKind kind, int index, String usageDescription) { + + List kindList = usages.get(kind).get(index); + kindList.add(usageDescription); + + return kindList.size() == 1; + } + + public Map>> getUsages() { + return Collections.unmodifiableMap(usages); + } + + public List getUsages(ItemKind kind, int index) { + return Collections.unmodifiableList(usages.get(kind).get(index)); + } + + public List> getUsages(ItemKind kind) { + return Collections.unmodifiableList(usages.get(kind)); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/simple/ABCWalker.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/simple/ABCWalker.java new file mode 100644 index 000000000..2a1dc1f2a --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/usages/simple/ABCWalker.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2010-2023 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.usages.simple; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.types.ScriptInfo; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; +import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Stack; + +/** + * + * @author JPEXS + */ +public abstract class ABCWalker { + + public static enum WalkType { + Orphan, + Script, + Class, + Instance + } + + public final void walkABC(ABC abc, boolean walkOrphanItems) { + Set handledClasses = new HashSet<>(); + Set handledMethodInfos = new HashSet<>(); + Set handledMethodBodies = new HashSet<>(); + + for (int i = 0; i < abc.script_info.size(); i++) { + handleScript(abc, i); + ScriptInfo si = abc.script_info.get(i); + optionalHandleMethodInfo(handledMethodBodies, handledMethodInfos, abc, si.init_index, i, -1, -1, -1, WalkType.Script, true, new Stack<>()); + walkTraits(abc, si.traits, i, -1, -1, -1, -1, handledMethodBodies, handledMethodInfos, handledClasses, WalkType.Script, new Stack<>()); + } + + if (walkOrphanItems) { + for (int i = 0; i < abc.method_info.size(); i++) { + optionalHandleMethodInfo(handledMethodBodies, handledMethodInfos, abc, i, -1, -1, -1, -1, WalkType.Orphan, false, new Stack<>()); + } + + for (int i = 0; i < abc.bodies.size(); i++) { + optionalHandleMethodBody(handledMethodBodies, handledMethodInfos, abc, i, -1, -1, -1, -1, WalkType.Orphan, false, new Stack<>()); + } + + for (int i = 0; i < abc.class_info.size(); i++) { + optionalHandleClass(handledClasses, handledMethodBodies, handledMethodInfos, abc, i, -1, -1, -1, WalkType.Orphan); + } + } + } + + private boolean optionalHandleClass(Set handledClasses, Set handledMethodBodies, Set handledMethodInfos, ABC abc, int index, int scriptIndex, int scriptTraitIndex, int traitIndex, WalkType walkType) { + if (handledClasses.contains(index)) { + return false; + } + handleClass(abc, index, scriptIndex, traitIndex, walkType); + + optionalHandleMethodInfo(handledMethodBodies, handledMethodInfos, abc, abc.instance_info.get(index).iinit_index, scriptIndex, scriptTraitIndex, index, -1, WalkType.Instance, true, new Stack<>()); + walkTraits(abc, abc.instance_info.get(index).instance_traits, scriptIndex, scriptTraitIndex, index, -1, -1, handledMethodBodies, handledMethodInfos, handledClasses, WalkType.Instance, new Stack<>()); + optionalHandleMethodInfo(handledMethodBodies, handledMethodInfos, abc, abc.class_info.get(index).cinit_index, scriptIndex, scriptTraitIndex, index, -1, WalkType.Class, true, new Stack<>()); + walkTraits(abc, abc.class_info.get(index).static_traits, scriptIndex, scriptTraitIndex, index, -1, -1, handledMethodBodies, handledMethodInfos, handledClasses, WalkType.Class, new Stack<>()); + + return true; + } + + private void optionalHandleMethodInfo(Set handledMethodBodies, Set handledMethodInfos, ABC abc, int index, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, WalkType walkType, boolean initializer, Stack callStack) { + if (handledMethodInfos.contains(index)) { + return; + } + handledMethodInfos.add(index); + if (callStack != null) { + if (callStack.contains(index)) { + return; + } + callStack.push(index); + } + handleMethodInfo(abc, index, scriptIndex, scriptTraitIndex, classIndex, traitIndex, walkType, initializer, callStack); + int bodyIndex = abc.findBodyIndex(index); + if (bodyIndex > -1) { + optionalHandleMethodBody(handledMethodBodies, handledMethodInfos, abc, bodyIndex, scriptIndex, scriptTraitIndex, classIndex, traitIndex, walkType, initializer, callStack); + } + if (callStack != null) { + callStack.pop(); + } + } + + private void optionalHandleMethodBody(Set handledMethodBodies, Set handledMethodInfos, ABC abc, int index, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, WalkType walkType, boolean initializer, Stack callStack) { + if (handledMethodBodies.contains(index)) { + return; + } + handledMethodBodies.add(index); + handleMethodBody(abc, index, scriptIndex, classIndex, traitIndex, walkType, initializer); + List code = abc.bodies.get(index).getCode().code; + for (AVM2Instruction ins : code) { + for (int o = 0; o < ins.definition.operands.length; o++) { + if (ins.definition.operands[o] == AVM2Code.DAT_METHOD_INDEX) { + optionalHandleMethodInfo(handledMethodBodies, handledMethodInfos, abc, ins.operands[o], scriptIndex, scriptTraitIndex, classIndex, traitIndex, walkType, initializer, callStack); + } + } + } + walkTraits(abc, abc.bodies.get(index).traits, scriptIndex, scriptTraitIndex, classIndex, traitIndex, index, handledMethodBodies, handledMethodInfos, handledMethodInfos, walkType, callStack); + } + private void walkTraits(ABC abc, Traits traits, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, int bodyIndex, Set handledMethodBodies, Set handledMethodInfos, Set handledClasses, WalkType walkType, Stack callStack) { + + int bodyTraitIndex = -1; + + for (int i = 0; i < traits.traits.size(); i++) { + if (classIndex == -1) { + scriptTraitIndex = i; + } + if (bodyIndex == -1 && classIndex != -1) { + traitIndex = i; + } + if (bodyIndex != -1) { + bodyTraitIndex = i; + } + Trait t = traits.traits.get(i); + if ((t.kindFlags & Trait.ATTR_Metadata) > 0) { + for (int m = 0; m < t.metadata.length; m++) { + handleMetadataInfo(abc, t.metadata[m], t, scriptIndex, scriptTraitIndex, classIndex, i, m, walkType); + } + } + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + handleTraitClass(abc, tc, scriptIndex, i); + int subClassIndex = tc.class_info; + optionalHandleClass(handledClasses, handledMethodBodies, handledMethodInfos, abc, subClassIndex, scriptIndex, scriptTraitIndex, i, walkType); + } + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t; + optionalHandleMethodInfo(handledMethodBodies, handledMethodInfos, abc, tm.method_info, scriptIndex, scriptTraitIndex, classIndex, i, walkType, false, new Stack<>()); + handleTraitMethodGetterSetter(abc, tm, scriptIndex, scriptTraitIndex, classIndex, i, walkType); + } + if (t instanceof TraitFunction) { + TraitFunction tf = (TraitFunction) t; + optionalHandleMethodInfo(handledMethodBodies, handledMethodInfos, abc, tf.method_info, scriptIndex, scriptTraitIndex, classIndex, i, walkType, false, new Stack<>()); + handleTraitFunction(abc, tf, scriptIndex, scriptTraitIndex, classIndex, i, walkType); + } + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + handleTraitSlotConst(abc, tsc, scriptIndex, scriptTraitIndex, classIndex, traitIndex, bodyIndex, bodyTraitIndex, walkType, callStack); + } + } + } + + protected void handleMethodInfo(ABC abc, int index, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, WalkType walkType, boolean initializer, Stack callStack) { + + } + + protected void handleMethodBody(ABC abc, int index, int scriptIndex, int classIndex, int traitIndex, WalkType walkType, boolean initializer) { + + } + + protected void handleClass(ABC abc, int index, int scriptIndex, int traitIndex, WalkType walkType) { + + } + + protected void handleScript(ABC abc, int index) { + + } + + protected void handleTraitSlotConst(ABC abc, TraitSlotConst trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, int bodyIndex, int bodyTraitIndex, WalkType walkType, Stack callStack) { + + } + + protected void handleTraitMethodGetterSetter(ABC abc, TraitMethodGetterSetter trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, WalkType walkType) { + + } + + protected void handleTraitFunction(ABC abc, TraitFunction trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, WalkType walkType) { + + } + + protected void handleTraitClass(ABC abc, TraitClass trait, int scriptIndex, int scriptTraitIndex) { + + } + + protected void handleMetadataInfo(ABC abc, int index, Trait trait, int scriptIndex, int scriptTraitIndex, int classIndex, int traitIndex, int traitMetadataIndex, WalkType walkType) { + + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ABCExplorerDialog.java b/src/com/jpexs/decompiler/flash/gui/abc/ABCExplorerDialog.java index f9a29cf93..795b8ed2e 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ABCExplorerDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ABCExplorerDialog.java @@ -38,11 +38,13 @@ import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.abc.usages.simple.ABCSimpleUsageDetector; import com.jpexs.decompiler.flash.ecma.EcmaScript; import com.jpexs.decompiler.flash.gui.AppDialog; import com.jpexs.decompiler.flash.gui.FasterScrollPane; import com.jpexs.decompiler.flash.gui.MainPanel; import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.ViewMessages; import com.jpexs.decompiler.flash.helpers.CodeFormatting; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.StringBuilderTextWriter; @@ -53,6 +55,7 @@ import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.treeitems.Openable; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.Helper; +import java.awt.AlphaComposite; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; @@ -60,12 +63,15 @@ import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.Image; import java.awt.Toolkit; import java.awt.Window; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayDeque; @@ -77,18 +83,30 @@ import java.util.Map; import java.util.Objects; import java.util.Queue; import java.util.Set; +import javax.swing.AbstractAction; +import javax.swing.ActionMap; import javax.swing.ImageIcon; +import javax.swing.InputMap; import javax.swing.JComboBox; +import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import javax.swing.JRootPane; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; import javax.swing.JTabbedPane; +import javax.swing.JTable; import javax.swing.JTree; +import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.event.TreeModelListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; import javax.swing.plaf.basic.BasicLabelUI; import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.table.DefaultTableModel; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; @@ -109,6 +127,15 @@ public class ABCExplorerDialog extends AppDialog { private Runnable packListener; + private ABCSimpleUsageDetector usageDetector = null; + + private JTable usagesTable = new JTable(new DefaultTableModel()) { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + }; + public ABCExplorerDialog(Window owner, MainPanel mainPanel, Openable openable, ABC abc) { super(owner); this.mainPanel = mainPanel; @@ -167,6 +194,7 @@ public class ABCExplorerDialog extends AppDialog { abcComboBoxActionPerformed(null); cpTabbedPane.setSelectedIndex(cpIndex); mainTabbedPane.setSelectedIndex(mainIndex); + refreshUsages(); } }; @@ -175,9 +203,87 @@ public class ABCExplorerDialog extends AppDialog { mainTabbedPane = new JTabbedPane(); cpTabbedPane = new JTabbedPane(); - + + DefaultTableModel model = new DefaultTableModel(); + model.addColumn(translate("usages").replace("%item%", "-")); + usagesTable.setModel(model); + + usagesTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) { + int row = usagesTable.getSelectedRow(); + if (row == -1) { + return; + } + String path = (String) usagesTable.getModel().getValueAt(row, 0); + selectPath(path); + } + } + }); + + usagesTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + int row = usagesTable.rowAtPoint( e.getPoint() ); + int column = usagesTable.columnAtPoint( e.getPoint() ); + if (!usagesTable.isRowSelected(row)) { + usagesTable.changeSelection(row, column, false, false); + } + JPopupMenu popupMenu = new JPopupMenu(); + JMenuItem hilightMenuItem = new JMenuItem(translate("hilight.usage"), View.getIcon("jumpto16")); + hilightMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int row = usagesTable.getSelectedRow(); + if (row == -1) { + return; + } + selectPath((String) usagesTable.getModel().getValueAt(row, 0)); + } + }); + JMenuItem copyMenuItem = new JMenuItem(translate("copy.paths"), View.getIcon("copy16")); + copyMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int[] rows = usagesTable.getSelectedRows(); + List values = new ArrayList<>(); + for (int row : rows) { + values.add((String)usagesTable.getModel().getValueAt(row, 0)); + } + copyToClipboard(String.join("\r\n", values)); + } + }); + JMenuItem copyAllMenuItem = new JMenuItem(translate("copy.paths.all"), View.getIcon("copy16")); + copyAllMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + List values = new ArrayList<>(); + for (int row = 0; row < usagesTable.getModel().getRowCount(); row++) { + values.add((String)usagesTable.getModel().getValueAt(row, 0)); + } + copyToClipboard(String.join("\r\n", values)); + } + }); + popupMenu.add(hilightMenuItem); + popupMenu.add(copyMenuItem); + popupMenu.add(copyAllMenuItem); + popupMenu.show(e.getComponent(), e.getX(), e.getY()); + } + } + }); + + JPanel centralPanel = new JPanel(new BorderLayout()); + JPanel rightPanel = new JPanel(new BorderLayout()); + rightPanel.add(new FasterScrollPane(usagesTable, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), BorderLayout.CENTER); + //rightPanel.add(calculateUsagesButton, BorderLayout.SOUTH); + JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, mainTabbedPane, rightPanel); + splitPane.setDividerLocation(800); + centralPanel.add(splitPane); + cnt.add(topPanel, BorderLayout.NORTH); - cnt.add(mainTabbedPane, BorderLayout.CENTER); + cnt.add(centralPanel, BorderLayout.CENTER); if (!abcs.isEmpty()) { if (abcComboBox != null) { @@ -186,7 +292,25 @@ public class ABCExplorerDialog extends AppDialog { abcComboBoxActionPerformed(null); } } - setSize(800, 600); + + JRootPane rootPane = getRootPane(); + KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G, KeyEvent.CTRL_DOWN_MASK); + InputMap inputMap = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); + ActionMap actionMap = rootPane.getActionMap(); + + inputMap.put(keyStroke, "ctrlGAction"); + actionMap.put("ctrlGAction", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + String path = ViewMessages.showInputDialog(ABCExplorerDialog.this, translate("goto.path.label"), translate("goto.path"), ""); + if (path == null || path.isEmpty()) { + return; + } + selectPath(path); + } + }); + + setSize(1024, 600); setTitle(translate("title") + " - " + openable.getTitleOrShortFileName()); List images = new ArrayList<>(); images.add(View.loadImage("abcexplorer16")); @@ -222,6 +346,11 @@ public class ABCExplorerDialog extends AppDialog { } private void abcComboBoxActionPerformed(ActionEvent e) { + usageDetector = null; + DefaultTableModel model = new DefaultTableModel(); + model.addColumn(translate("usages").replace("%item%", "-")); + usagesTable.setModel(model); + int index = abcComboBox == null ? 0 : abcComboBox.getSelectedIndex(); if (index == -1) { return; @@ -288,7 +417,15 @@ public class ABCExplorerDialog extends AppDialog { mainTabbedPane.addTab("mb (" + abc.bodies.size() + ")", View.getIcon(TreeType.METHOD_BODY.getIcon().getFile()), makeTreePanel(abc, TreeType.METHOD_BODY)); abc.removeChangeListener(packListener); - abc.addChangeListener(packListener); + abc.addChangeListener(packListener); + refreshUsages(); + repaint(); + } + + private void refreshUsages() { + ABCSimpleUsageDetector newUsageDetector = new ABCSimpleUsageDetector(getSelectedAbc()); + newUsageDetector.detect(); + usageDetector = newUsageDetector; } private JTree getCurrentTree() { @@ -302,6 +439,38 @@ public class ABCExplorerDialog extends AppDialog { JTree tree = (JTree) fasterScrollPane.getViewport().getView(); return tree; } + + private String getCurrentPath() { + JTree tree = getCurrentTree(); + TreePath tp = tree.getSelectionPath(); + if (tp == null) { + return ""; + } + List pathParts = new ArrayList<>(); + Object[] path = tp.getPath(); + for (int i = 1; i < path.length; i++) { + Object child = path[i]; + String key; + if (child instanceof ValueWithIndex) { + ValueWithIndex vwi = (ValueWithIndex) child; + if (!vwi.getTitle().isEmpty()) { + key = vwi.getTitle(); + } else { + key = vwi.getType().getAbbreviation() + vwi.getIndex(); + } + } else if (child instanceof SubValue) { + SubValue sv = (SubValue) child; + key = sv.getTitle(); + } else if (child instanceof SimpleValue) { + SimpleValue sv = (SimpleValue) child; + key = sv.getTitle(); + } else { + break; + } + pathParts.add(key); + } + return String.join("/", pathParts); + } public void selectTrait(int scriptIndex, int classIndex, int traitIndex, int traitType) { selectScriptInfo(scriptIndex); @@ -347,14 +516,113 @@ public class ABCExplorerDialog extends AppDialog { return -1; } + private void selectPath(String path) { + String[] parts = path.split("/"); + String mainItem = parts[0]; + TreeType selectedType = null; + for (TreeType type : TreeType.values()) { + if (mainItem.startsWith(type.getAbbreviation())) { + selectedType = type; + } + } + if (selectedType == null) { + return; + } + + if (mainTabbedPane.getTabCount() == 0) { + return; + } + + int stringOffset = 0; + if (getSelectedAbc().hasDecimalSupport()) { + stringOffset = 1; + } + if (getSelectedAbc().hasFloatSupport()) { + stringOffset = 2; + } + switch (selectedType) { + case CONSTANT_INT: + mainTabbedPane.setSelectedIndex(0); + cpTabbedPane.setSelectedIndex(0); + break; + case CONSTANT_UINT: + mainTabbedPane.setSelectedIndex(0); + cpTabbedPane.setSelectedIndex(1); + break; + case CONSTANT_DOUBLE: + mainTabbedPane.setSelectedIndex(0); + cpTabbedPane.setSelectedIndex(2); + break; + case CONSTANT_DECIMAL: + if (!getSelectedAbc().hasDecimalSupport()) { + return; + } + mainTabbedPane.setSelectedIndex(0); + cpTabbedPane.setSelectedIndex(3); + break; + case CONSTANT_FLOAT: + if (!getSelectedAbc().hasFloatSupport()) { + return; + } + mainTabbedPane.setSelectedIndex(0); + cpTabbedPane.setSelectedIndex(3); + break; + case CONSTANT_FLOAT_4: + if (!getSelectedAbc().hasFloatSupport()) { + return; + } + mainTabbedPane.setSelectedIndex(0); + cpTabbedPane.setSelectedIndex(4); + break; + case CONSTANT_STRING: + mainTabbedPane.setSelectedIndex(0); + cpTabbedPane.setSelectedIndex(3 + stringOffset); + break; + case CONSTANT_NAMESPACE: + mainTabbedPane.setSelectedIndex(0); + cpTabbedPane.setSelectedIndex(4 + stringOffset); + break; + case CONSTANT_NAMESPACE_SET: + mainTabbedPane.setSelectedIndex(0); + cpTabbedPane.setSelectedIndex(5 + stringOffset); + break; + case CONSTANT_MULTINAME: + mainTabbedPane.setSelectedIndex(0); + cpTabbedPane.setSelectedIndex(6 + stringOffset); + break; + case METHOD_INFO: + mainTabbedPane.setSelectedIndex(1); + break; + case METADATA_INFO: + mainTabbedPane.setSelectedIndex(2); + break; + case INSTANCE_INFO: + mainTabbedPane.setSelectedIndex(3); + break; + case CLASS_INFO: + mainTabbedPane.setSelectedIndex(4); + break; + case SCRIPT_INFO: + mainTabbedPane.setSelectedIndex(5); + break; + case METHOD_BODY: + mainTabbedPane.setSelectedIndex(6); + break; + } + + selectPath(getCurrentTree(), path); + } + private void selectPath(JTree tree, String path) { String[] parts = path.split("/"); TreeModel model = tree.getModel(); Object root = model.getRoot(); Object parent = root; - Object[] treePathObjects = new Object[parts.length + 1]; - treePathObjects[0] = root; + List treePathObjectsList = new ArrayList<>(); + /*Object[] treePathObjects = new Object[parts.length + 1]; + treePathObjects[0] = root;*/ + treePathObjectsList.add(root); loopp: for (int p = 0; p < parts.length; p++) { @@ -377,14 +645,14 @@ public class ABCExplorerDialog extends AppDialog { key = sv.getTitle(); } if (key.equals(part)) { - treePathObjects[1 + p] = child; + //treePathObjects[1 + p] = child; + treePathObjectsList.add(child); parent = child; continue loopp; } - } - return; + } } - TreePath treePath = new TreePath(treePathObjects); + TreePath treePath = new TreePath(treePathObjectsList.toArray(new Object[treePathObjectsList.size()])); tree.setSelectionPath(treePath); tree.scrollPathToVisible(treePath); } @@ -414,7 +682,7 @@ public class ABCExplorerDialog extends AppDialog { if (View.isOceanic()) { tree.setBackground(Color.white); } - tree.setCellRenderer(new ExplorerTreeCellRenderer()); + tree.setCellRenderer(new ExplorerTreeCellRenderer(this)); tree.setUI(new BasicTreeUI() { { if (View.isOceanic()) { @@ -422,6 +690,41 @@ public class ABCExplorerDialog extends AppDialog { } } }); + tree.addTreeSelectionListener(new TreeSelectionListener() { + @Override + public void valueChanged(TreeSelectionEvent e) { + DefaultTableModel model = new DefaultTableModel(); + + if (tree.getSelectionCount() != 1 || usageDetector == null) { + //usagesList.setModel(new DefaultListModel<>()); + model.addColumn(translate("usages").replace("%item%", "-")); + usagesTable.setModel(model); + return; + } + + Object selection = tree.getLastSelectedPathComponent(); + + if (selection instanceof ValueWithIndex) { + ValueWithIndex vwi = (ValueWithIndex) selection; + if (vwi.getType().getUsageKind() != null) { + List newUsages = usageDetector.getUsages(vwi.type.getUsageKind(), vwi.index); + //DefaultListModel model = new DefaultListModel<>(); + //model.addAll(newUsages); + //usagesList.setModel(model); + model.addColumn(translate("usages").replace("%item%", vwi.type.getAbbreviation() + vwi.index + ": " + vwi.getDescription())); + for (String usage : newUsages) { + model.addRow(new Object[] {usage}); + } + usagesTable.setModel(model); + } else { + model.addColumn(translate("usages").replace("%item%", "-")); + } + } else { + model.addColumn(translate("usages").replace("%item%", "-")); + } + usagesTable.setModel(model); + } + }); tree.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { @@ -447,8 +750,11 @@ public class ABCExplorerDialog extends AppDialog { if (tree.getSelectionCount() != 1) { return null; } + + + Object selection = tree.getLastSelectedPathComponent(); - + if (selection instanceof ValueWithIndex) { ValueWithIndex vwi = (ValueWithIndex) selection; if (vwi.getType() == TreeType.SCRIPT_INFO) { @@ -488,7 +794,12 @@ public class ABCExplorerDialog extends AppDialog { break; } } - if (selection != null) { + + if (selection != null) { + JMenuItem copyMenuItem = new JMenuItem(translate("copy.path"), View.getIcon("copy16")); + copyMenuItem.addActionListener(this::copyPathActionPerformed); + menu.add(copyMenuItem); + JMenuItem copyRowMenuItem = new JMenuItem(translate("copy.row"), View.getIcon("copy16")); copyRowMenuItem.addActionListener(this::copyRowActionPerformed); menu.add(copyRowMenuItem); @@ -544,6 +855,10 @@ public class ABCExplorerDialog extends AppDialog { return menu; } + private void copyPathActionPerformed(ActionEvent e) { + copyToClipboard(getCurrentPath()); + } + private void copyRowActionPerformed(ActionEvent e) { Object selection = getCurrentTree().getLastSelectedPathComponent(); if (selection != null) { @@ -551,8 +866,6 @@ public class ABCExplorerDialog extends AppDialog { } } - - private boolean showMethodInfoTraits(int round, int scriptIndex, int classIndex, int methodInfo, ABC abc, Traits traits, int traitsType, int scriptTraitIndex) { for (int j = 0; j < traits.traits.size(); j++) { Trait t = (Trait) traits.traits.get(j); @@ -638,7 +951,7 @@ public class ABCExplorerDialog extends AppDialog { } return found; } - + private void showMethodInfo(int methodInfo) { ABC abc = getSelectedAbc(); for (int round = 1; round <= 2; round++) { @@ -883,31 +1196,33 @@ public class ABCExplorerDialog extends AppDialog { } private enum TreeType { - CONSTANT_INT("Integers", "int", TreeIcon.CONSTANT_INT), - CONSTANT_UINT("UnsignedIntegers", "uint", TreeIcon.CONSTANT_UINT), - CONSTANT_DOUBLE("Doubles", "dbl", TreeIcon.CONSTANT_DOUBLE), - CONSTANT_DECIMAL("Decimals", "dc", TreeIcon.CONSTANT_DECIMAL), //needs ABC decimal support - CONSTANT_FLOAT("Floats", "fl", TreeIcon.CONSTANT_FLOAT), //needs ABC float support - CONSTANT_FLOAT_4("Floats4", "fl4", TreeIcon.CONSTANT_FLOAT_4), //needs ABC float support - CONSTANT_STRING("Strings", "str", TreeIcon.CONSTANT_STRING), - CONSTANT_NAMESPACE("Namespaces", "ns", TreeIcon.CONSTANT_NAMESPACE), - CONSTANT_NAMESPACE_SET("NamespaceSets", "nss", TreeIcon.CONSTANT_NAMESPACE_SET), - CONSTANT_MULTINAME("Multinames", "mn", TreeIcon.CONSTANT_MULTINAME), - METHOD_INFO("MethodInfos", "mi", TreeIcon.METHOD_INFO), - METADATA_INFO("MetadataInfos", "md", TreeIcon.METADATA_INFO), - INSTANCE_INFO("InstanceInfos", "ii", TreeIcon.INSTANCE_INFO), - CLASS_INFO("ClassInfos", "ci", TreeIcon.CLASS_INFO), - SCRIPT_INFO("ScriptInfos", "si", TreeIcon.SCRIPT_INFO), - METHOD_BODY("MethodBodys", "mb", TreeIcon.METHOD_BODY); + CONSTANT_INT("Integers", "int", TreeIcon.CONSTANT_INT, ABCSimpleUsageDetector.ItemKind.INT), + CONSTANT_UINT("UnsignedIntegers", "uint", TreeIcon.CONSTANT_UINT, ABCSimpleUsageDetector.ItemKind.UINT), + CONSTANT_DOUBLE("Doubles", "dbl", TreeIcon.CONSTANT_DOUBLE, ABCSimpleUsageDetector.ItemKind.DOUBLE), + CONSTANT_DECIMAL("Decimals", "dc", TreeIcon.CONSTANT_DECIMAL, null), //needs ABC decimal support + CONSTANT_FLOAT("Floats", "fl", TreeIcon.CONSTANT_FLOAT, null), //needs ABC float support + CONSTANT_FLOAT_4("Floats4", "fl4", TreeIcon.CONSTANT_FLOAT_4, null), //needs ABC float support + CONSTANT_STRING("Strings", "str", TreeIcon.CONSTANT_STRING, ABCSimpleUsageDetector.ItemKind.STRING), + CONSTANT_NAMESPACE("Namespaces", "ns", TreeIcon.CONSTANT_NAMESPACE, ABCSimpleUsageDetector.ItemKind.NAMESPACE), + CONSTANT_NAMESPACE_SET("NamespaceSets", "nss", TreeIcon.CONSTANT_NAMESPACE_SET, ABCSimpleUsageDetector.ItemKind.NAMESPACESET), + CONSTANT_MULTINAME("Multinames", "mn", TreeIcon.CONSTANT_MULTINAME, ABCSimpleUsageDetector.ItemKind.MULTINAME), + METHOD_INFO("MethodInfos", "mi", TreeIcon.METHOD_INFO, ABCSimpleUsageDetector.ItemKind.METHODINFO), + METADATA_INFO("MetadataInfos", "md", TreeIcon.METADATA_INFO, ABCSimpleUsageDetector.ItemKind.METADATAINFO), + INSTANCE_INFO("InstanceInfos", "ii", TreeIcon.INSTANCE_INFO, ABCSimpleUsageDetector.ItemKind.CLASS), + CLASS_INFO("ClassInfos", "ci", TreeIcon.CLASS_INFO, ABCSimpleUsageDetector.ItemKind.CLASS), + SCRIPT_INFO("ScriptInfos", "si", TreeIcon.SCRIPT_INFO, null), + METHOD_BODY("MethodBodys", "mb", TreeIcon.METHOD_BODY, ABCSimpleUsageDetector.ItemKind.METHODBODY); private final String name; private final String abbreviation; private final TreeIcon icon; + private final ABCSimpleUsageDetector.ItemKind usageKind; - TreeType(String name, String abbreviation, TreeIcon icon) { + TreeType(String name, String abbreviation, TreeIcon icon, ABCSimpleUsageDetector.ItemKind usageKind) { this.name = name; this.abbreviation = abbreviation; this.icon = icon; + this.usageKind = usageKind; } public String getName() { @@ -926,6 +1241,10 @@ public class ABCExplorerDialog extends AppDialog { public String toString() { return name; } + + public ABCSimpleUsageDetector.ItemKind getUsageKind() { + return usageKind; + } } private class SimpleValue implements HasIcon { @@ -2233,20 +2552,23 @@ public class ABCExplorerDialog extends AppDialog { public static class ExplorerTreeCellRenderer extends DefaultTreeCellRenderer { Map iconCache = new HashMap<>(); + boolean semiTransparent = false; + private final ABCExplorerDialog dialog; - public ExplorerTreeCellRenderer() { + public ExplorerTreeCellRenderer(ABCExplorerDialog dialog) { setUI(new BasicLabelUI()); setOpaque(false); if (View.isOceanic()) { setBackgroundNonSelectionColor(Color.white); } + this.dialog = dialog; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); - /*if (semiTransparent) { + if (semiTransparent) { if (getIcon() != null) { Color color = getBackground(); Graphics2D g2d = (Graphics2D) g; @@ -2254,7 +2576,7 @@ public class ABCExplorerDialog extends AppDialog { g2d.setComposite(AlphaComposite.SrcOver); g2d.fillRect(0, 0, getWidth(), getHeight()); } - }*/ + } } @Override @@ -2294,7 +2616,17 @@ public class ABCExplorerDialog extends AppDialog { setIcon(null); } - //semitransparent = true; + semiTransparent = false; + if (value instanceof ValueWithIndex) { + if (dialog.usageDetector != null) { + ValueWithIndex vwi = (ValueWithIndex) value; + if (vwi.getType().getUsageKind() != null) { + List usages = dialog.usageDetector.getUsages(vwi.getType().getUsageKind(), vwi.getIndex()); + semiTransparent = usages.isEmpty(); + } + } + + } return this; } } diff --git a/src/com/jpexs/decompiler/flash/gui/locales/abc/ABCExplorerDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/abc/ABCExplorerDialog.properties index 60f87d409..5e56866d1 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/abc/ABCExplorerDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/abc/ABCExplorerDialog.properties @@ -28,3 +28,13 @@ copy.typeid = Copy typeId to clipboard copy.title = Copy title to clipboard copy.value = Copy value to clipboard copy.rawstring = Copy raw string value to clipboard + +#after 20.1.0 +usages = Usages of %item% +copy.path = Copy path to clipboard +copy.paths = Copy selected paths to clipboard +copy.paths.all = Copy all paths to clipboard +hilight.usage = Hilight selected path + +goto.path = Go to path +goto.path.label = Enter path to navigate to \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/abc/ABCExplorerDialog_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/abc/ABCExplorerDialog_cs.properties index c856e63b2..85b87b75b 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/abc/ABCExplorerDialog_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/abc/ABCExplorerDialog_cs.properties @@ -28,3 +28,14 @@ copy.typeid = Kop\u00edrovat typeId do schr\u00e1nky copy.title = Kop\u00edrovat titulek do schr\u00e1nky copy.value = Kop\u00edrovat hodnotu do schr\u00e1nky copy.rawstring = Kop\u00edrovat raw \u0159et\u011bzcovou hodnotu do schr\u00e1nky + +#after 20.1.0 +usages = Pou\u017eit\u00ed %item% +button.usages.calculate = Spo\u010d\u00edtat pou\u017eit\u00ed pro toto ABC +copy.path = Kop\u00edrovat cestu do schr\u00e1nky +copy.paths = Kop\u00edrovat vybran\u00e9 cesty do schr\u00e1nky +copy.paths.all = Kop\u00edrovat v\u0161echny cesty do schr\u00e1nky +hilight.usage = Zv\u00fdraznit vybranou cestu + +goto.path = P\u0159ej\u00edt na cestu +goto.path.label = Zadejte cestu kam p\u0159ej\u00edt \ No newline at end of file