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 f47e3fcfb..e0c00d473 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
@@ -134,6 +134,8 @@ public class ABC implements Openable {
private OpenableList openableList;
private boolean isOpenable = false;
+
+ private long dataSize = 0L;
public ABC(ABCContainerTag tag) {
this.parentTag = tag;
@@ -570,11 +572,14 @@ public class ABC implements Openable {
isOpenable = true;
}
+ long posBefore = ais.getPosition();
try {
read(ais, swf);
} catch (IOException ie) {
throw new ABCOpenException(AppResources.translate("error.abc.invalid"), ie);
}
+ long posAfter = ais.getPosition();
+ dataSize = posAfter - posBefore;
//this will read all method body codes. TODO: make this ondemand
refreshMultinameNamespaceSuffixes();
getMethodIndexing();
@@ -808,7 +813,7 @@ public class ABC implements Openable {
}
public void saveToStream(OutputStream os) throws IOException {
- ABCOutputStream aos = new ABCOutputStream(os);
+ ABCOutputStream aos = new ABCOutputStream(os);
aos.writeU16(version.minor);
aos.writeU16(version.major);
@@ -918,6 +923,7 @@ public class ABC implements Openable {
}
aos.writeTraits(mb.traits);
}
+ dataSize = aos.getPosition();
}
public MethodBody findBody(MethodInfo methodInfo) {
@@ -2476,4 +2482,8 @@ public class ABC implements Openable {
}
return false;
}
+
+ public long getDataSize() {
+ return dataSize;
+ }
}
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCOutputStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCOutputStream.java
index f69d9d1f2..4303c0124 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCOutputStream.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ABCOutputStream.java
@@ -39,16 +39,34 @@ import java.io.OutputStream;
public class ABCOutputStream extends OutputStream {
private final OutputStream os;
+ private long position = 0L;
public ABCOutputStream(OutputStream os) {
this.os = os;
}
+ public long getPosition() {
+ return position;
+ }
+
@Override
public void write(int b) throws IOException {
os.write(b);
+ position++;
+ }
+
+ @Override
+ public void write(byte[] data) throws IOException {
+ super.write(data);
+ position += data.length;
}
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ super.write(b, off, len);
+ position += len;
+ }
+
public void writeU30(long value) throws IOException {
writeS32(value);
/*boolean loop = true;
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 d605be338..a429b0c30 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
@@ -269,7 +269,7 @@ public class Multiname {
return constants.getNamespace(index).getKindStr() + "(" + (name == null ? "null" : "\"" + Helper.escapePCodeString(name) + "\"") + (sub > 0 ? ",\"" + sub + "\"" : "") + ")";
}
- private static String namespaceSetToString(AVM2ConstantPool constants, int index) {
+ public static String namespaceSetToString(AVM2ConstantPool constants, int index) {
if (index == 0) {
return "null";
}
diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java
index d04ed735a..2668e996f 100644
--- a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java
+++ b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java
@@ -21,9 +21,11 @@ import com.jpexs.decompiler.flash.ApplicationInfo;
import com.jpexs.decompiler.flash.Bundle;
import com.jpexs.decompiler.flash.OpenableSourceInfo;
import com.jpexs.decompiler.flash.SWF;
+import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.configuration.ConfigurationItemChangeListener;
import com.jpexs.decompiler.flash.console.ContextMenuTools;
+import com.jpexs.decompiler.flash.gui.abc.ABCExplorerDialog;
import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools;
import com.jpexs.decompiler.flash.gui.helpers.CheckResources;
import com.jpexs.decompiler.flash.search.ScriptSearchResult;
@@ -1031,8 +1033,9 @@ public abstract class MainFrameMenu implements MenuBuilder {
setMenuEnabled("/tools/search", openableSelected);
setMenuEnabled("/tools/replace", swfSelected);
setMenuEnabled("/tools/timeline", swfSelected);
+ setMenuEnabled("/tools/abcExplorer", openableSelected);
setMenuEnabled("/tools/showProxy", !isWorking);
-
+
setMenuEnabled("/tools/gotoDocumentClass", hasAbc);
/*setMenuEnabled("/tools/debugger/debuggerSwitch", hasAbc);
setMenuChecked("/tools/debugger/debuggerSwitch", hasDebugger);
@@ -1219,6 +1222,7 @@ public abstract class MainFrameMenu implements MenuBuilder {
addMenuItem("/tools/replace", translate("menu.tools.replace"), "replace32", this::replaceActionPerformed, PRIORITY_TOP, null, true, null, false);
addToggleMenuItem("/tools/timeline", translate("menu.tools.timeline"), null, "timeline32", this::timelineActionPerformed, PRIORITY_TOP, null);
+ addMenuItem("/tools/abcExplorer", translate("menu.tools.abcexplorer"), "abc32", this::abcExplorerActionPerformed, PRIORITY_TOP, null, true, null, false);
addMenuItem("/tools/showProxy", translate("menu.tools.proxy"), "proxy16", this::showProxyActionPerformed, PRIORITY_MEDIUM, null, true, null, false);
if (Platform.isWindows()) {
addMenuItem("/tools/searchMemory", translate("menu.tools.searchMemory"), "loadmemory16", this::searchMemoryActionPerformed, PRIORITY_MEDIUM, null, true, null, false);
@@ -1673,6 +1677,21 @@ public abstract class MainFrameMenu implements MenuBuilder {
return true;
}
+ protected void abcExplorerActionPerformed(ActionEvent evt) {
+
+ SWF swf;
+ if (openable instanceof SWF) {
+ swf = (SWF) openable;
+ } else if (openable instanceof ABC) {
+ swf = ((ABC)openable).getSwf();
+ } else {
+ return;
+ }
+
+ ABCExplorerDialog aed = new ABCExplorerDialog(Main.getMainFrame().getWindow(), swf, null);
+ aed.setVisible(true);
+ }
+
public boolean stackActionPerformed(ActionEvent evt) {
//TODO
return true;
diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ABCExplorerDialog.java b/src/com/jpexs/decompiler/flash/gui/abc/ABCExplorerDialog.java
new file mode 100644
index 000000000..215071674
--- /dev/null
+++ b/src/com/jpexs/decompiler/flash/gui/abc/ABCExplorerDialog.java
@@ -0,0 +1,594 @@
+/*
+ * Copyright (C) 2023 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.SWF;
+import com.jpexs.decompiler.flash.abc.ABC;
+import com.jpexs.decompiler.flash.abc.types.Float4;
+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.ecma.EcmaScript;
+import com.jpexs.decompiler.flash.gui.AppDialog;
+import com.jpexs.decompiler.flash.gui.FasterScrollPane;
+import com.jpexs.decompiler.flash.gui.View;
+import com.jpexs.decompiler.flash.tags.ABCContainerTag;
+import com.jpexs.decompiler.flash.tags.DoABC2Tag;
+import com.jpexs.decompiler.flash.tags.ShowFrameTag;
+import com.jpexs.decompiler.flash.tags.Tag;
+import com.jpexs.decompiler.graph.DottedChain;
+import com.jpexs.helpers.Helper;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Graphics;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import javax.swing.ComboBoxModel;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.JTree;
+import javax.swing.event.TreeModelListener;
+import javax.swing.plaf.basic.BasicLabelUI;
+import javax.swing.plaf.basic.BasicTreeUI;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+
+/**
+ *
+ * @author JPEXS
+ */
+public class ABCExplorerDialog extends AppDialog {
+
+ private final List abcContainers = new ArrayList<>();
+ private final JComboBox abcComboBox;
+ private final JLabel tagInfoLabel;
+ private final List abcFrames = new ArrayList<>();
+ private final JTabbedPane mainTabbedPane;
+ private final JTabbedPane cpTabbedPane;
+
+ public ABCExplorerDialog(Window owner, SWF swf, ABC abc) {
+ super(owner);
+ Container cnt = getContentPane();
+ cnt.setLayout(new BorderLayout());
+ JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ topPanel.add(new JLabel(translate("abc")));
+ int frame = 1;
+ for (Tag t : swf.getTags()) {
+ if (t instanceof ShowFrameTag) {
+ frame++;
+ }
+ if (t instanceof ABCContainerTag) {
+ abcContainers.add((ABCContainerTag)t);
+ abcFrames.add(frame);
+ }
+ }
+ String[] abcComboBoxData = new String[abcContainers.size()];
+ for (int i = 0; i < abcContainers.size(); i++) {
+ abcComboBoxData[i] = "tag" + (i+1);
+ if (abcContainers.get(i) instanceof DoABC2Tag) {
+ DoABC2Tag doa2 = (DoABC2Tag)abcContainers.get(i);
+ if (doa2.name != null && !doa2.name.isEmpty()) {
+ abcComboBoxData[i] += " (\"" + Helper.escapePCodeString(doa2.name) + "\")";
+ }
+ }
+ }
+ abcComboBox = new JComboBox<>(abcComboBoxData);
+ Dimension abcComboBoxSize = new Dimension(500, abcComboBox.getPreferredSize().height);
+ abcComboBox.setMinimumSize(abcComboBoxSize);
+ abcComboBox.setPreferredSize(abcComboBoxSize);
+ topPanel.add(abcComboBox);
+
+ tagInfoLabel = new JLabel();
+ topPanel.add(tagInfoLabel);
+
+
+ abcComboBox.addActionListener(this::abcComboBoxActionPerformed);
+
+ mainTabbedPane = new JTabbedPane();
+ cpTabbedPane = new JTabbedPane();
+
+ cnt.add(topPanel, BorderLayout.NORTH);
+ cnt.add(mainTabbedPane, BorderLayout.CENTER);
+
+ abcComboBoxActionPerformed(null);
+ setSize(800, 600);
+ setTitle(translate("title"));
+ View.setWindowIcon(this);
+ View.centerScreen(this);
+ }
+
+ private ABC getSelectedAbc() {
+ return abcContainers.get(abcComboBox.getSelectedIndex()).getABC();
+ }
+
+ private void abcComboBoxActionPerformed(ActionEvent e)
+ {
+ int index = abcComboBox.getSelectedIndex();
+ ABC abc = abcContainers.get(index).getABC();
+ tagInfoLabel.setText(
+ translate("abc.info")
+ .replace("%index%", "" + (index + 1))
+ .replace("%count%", "" + abcComboBox.getItemCount())
+ .replace("%major%", "" + abc.version.major)
+ .replace("%minor%", "" + abc.version.minor)
+ .replace("%size%", Helper.formatFileSize(abc.getDataSize()))
+ .replace("%frame%", "" + abcFrames.get(index))
+ );
+
+
+ cpTabbedPane.removeAll();
+
+
+ cpTabbedPane.addTab("int (" + abc.constants.getIntCount() + ")", makeTreePanel(abc, TreeType.CONSTANT_INT));
+ cpTabbedPane.addTab("uint (" + abc.constants.getUIntCount() + ")", makeTreePanel(abc, TreeType.CONSTANT_UINT));
+ cpTabbedPane.addTab("dbl (" + abc.constants.getDoubleCount() + ")", makeTreePanel(abc, TreeType.CONSTANT_DOUBLE));
+ if (abc.hasDecimalSupport()) {
+ cpTabbedPane.addTab("dc (" + abc.constants.getDecimalCount() + ")", makeTreePanel(abc, TreeType.CONSTANT_DECIMAL));
+ }
+ if (abc.hasFloatSupport()) {
+ cpTabbedPane.addTab("fl (" + abc.constants.getFloatCount() + ")", makeTreePanel(abc, TreeType.CONSTANT_FLOAT));
+ cpTabbedPane.addTab("fl4 (" + abc.constants.getFloat4Count() + ")", makeTreePanel(abc, TreeType.CONSTANT_FLOAT_4));
+ }
+ cpTabbedPane.addTab("str (" + abc.constants.getStringCount() + ")", makeTreePanel(abc, TreeType.CONSTANT_STRING));
+ cpTabbedPane.addTab("ns (" + abc.constants.getNamespaceCount() + ")", makeTreePanel(abc, TreeType.CONSTANT_NAMESPACE));
+ cpTabbedPane.addTab("nss (" + abc.constants.getNamespaceSetCount() + ")", makeTreePanel(abc, TreeType.CONSTANT_NAMESPACE_SET));
+ cpTabbedPane.addTab("mn (" + abc.constants.getMultinameCount() + ")", makeTreePanel(abc, TreeType.CONSTANT_MULTINAME));
+
+ mainTabbedPane.removeAll();
+
+ JPanel cpPanel = new JPanel(new BorderLayout());
+ cpPanel.add(cpTabbedPane, BorderLayout.CENTER);
+
+ int cpCount = abc.constants.getIntCount() - 1 +
+ abc.constants.getUIntCount() - 1 +
+ abc.constants.getDoubleCount() - 1 +
+ abc.constants.getStringCount() - 1 +
+ abc.constants.getNamespaceCount() - 1 +
+ abc.constants.getNamespaceSetCount() - 1 +
+ abc.constants.getMultinameCount() - 1 +
+ (abc.hasDecimalSupport() ? abc.constants.getDecimalCount() - 1 : 0) +
+ (abc.hasFloatSupport()? abc.constants.getFloatCount() - 1 + abc.constants.getFloat4Count() - 1 : 0);
+ mainTabbedPane.addTab("cp (" + cpCount + ")", cpPanel);
+ mainTabbedPane.addTab("mi (" + abc.method_info.size() + ")", makeTreePanel(abc, TreeType.METHOD_INFO));
+ mainTabbedPane.addTab("md (" + abc.metadata_info.size() + ")", makeTreePanel(abc, TreeType.METADATA));
+ mainTabbedPane.addTab("ii (" + abc.instance_info.size() + ")", makeTreePanel(abc, TreeType.INSTANCE_INFO));
+ mainTabbedPane.addTab("ci (" + abc.class_info.size() + ")", makeTreePanel(abc, TreeType.CLASS_INFO));
+ mainTabbedPane.addTab("si (" + abc.script_info.size() + ")", makeTreePanel(abc, TreeType.SCRIPT_INFO));
+ mainTabbedPane.addTab("mb (" + abc.bodies.size() + ")", makeTreePanel(abc, TreeType.METHOD_BODY));
+ }
+
+ private JPanel makeTreePanel(ABC abc, TreeType type) {
+ JTree tree = new JTree(new ExplorerTreeModel(abc, type));
+ if (View.isOceanic()) {
+ tree.setBackground(Color.white);
+ }
+ tree.setCellRenderer(new ExplorerTreeCellRenderer());
+ tree.setUI(new BasicTreeUI() {
+ {
+ if (View.isOceanic()) {
+ setHashColor(Color.gray);
+ }
+ }
+ });
+
+ JPanel treePanel = new JPanel(new BorderLayout());
+ treePanel.add(new FasterScrollPane(tree), BorderLayout.CENTER);
+ return treePanel;
+ }
+
+ private enum TreeType {
+ CONSTANT_INT("Integers","int"),
+ CONSTANT_UINT("UnsignedIntegers","uint"),
+ CONSTANT_DOUBLE("Doubles", "dbl"),
+ CONSTANT_DECIMAL("Decimals","dc"), //needs ABC decimal support
+ CONSTANT_FLOAT("Floats","fl"), //needs ABC float support
+ CONSTANT_FLOAT_4("Floats4","fl4"), //needs ABC float support
+ CONSTANT_STRING("Strings","str"),
+ CONSTANT_NAMESPACE("Namespaces", "ns"),
+ CONSTANT_NAMESPACE_SET("NamespaceSets","nss"),
+ CONSTANT_MULTINAME("Multinames","mn"),
+ METHOD_INFO("MethodInfos","mi"),
+ METADATA("MetadataInfos","md"),
+ INSTANCE_INFO("InstanceInfos","ii"),
+ CLASS_INFO("ClassInfos","ci"),
+ SCRIPT_INFO("ScriptInfos","si"),
+ METHOD_BODY("MethodBodys","mb");
+
+ private final String name;
+ private final String abbreviation;
+
+ TreeType(String name, String abbreviation) {
+ this.name = name;
+ this.abbreviation = abbreviation;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getAbbreviation() {
+ return abbreviation;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ private class ValueWithIndex {
+ private final int index;
+ private final TreeType type;
+ private final Object value;
+ private final String title;
+ private final String prefix;
+
+ public ValueWithIndex(int index, TreeType type, Object value, String title) {
+ this.index = index;
+ this.type = type;
+ this.value = value;
+ this.title = title;
+ this.prefix = "";
+ }
+ public ValueWithIndex(int index, TreeType type, Object value, String title, String prefix) {
+ this.index = index;
+ this.type = type;
+ this.value = value;
+ this.title = title;
+ this.prefix = prefix;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public TreeType getType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ boolean implicit = false;
+ if (index == 0) {
+ switch (type) {
+ case CONSTANT_INT:
+ case CONSTANT_UINT:
+ case CONSTANT_DOUBLE:
+ case CONSTANT_STRING:
+ case CONSTANT_NAMESPACE:
+ case CONSTANT_MULTINAME:
+ implicit = true;
+ }
+ }
+
+ return prefix + (implicit ? "[" : "") +type.getAbbreviation() + index + (implicit ? "]" : "") + ": " + title;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 23 * hash + this.index;
+ hash = 23 * hash + Objects.hashCode(this.type);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final ValueWithIndex other = (ValueWithIndex) obj;
+ if (this.index != other.index) {
+ return false;
+ }
+ return this.type == other.type;
+ }
+
+
+ }
+
+ private class ExplorerTreeModel implements TreeModel {
+
+ private TreeType type;
+ private ABC abc;
+
+ public ExplorerTreeModel(ABC abc, TreeType type) {
+ this.type = type;
+ this.abc = abc;
+ }
+
+ @Override
+ public Object getRoot() {
+ return type;
+ }
+
+ private ValueWithIndex createValueWithIndex(int index, TreeType valueType, String prefix) {
+ if (index == 0) {
+ switch(valueType) {
+ case CONSTANT_INT:
+ case CONSTANT_UINT:
+ case CONSTANT_DOUBLE:
+ case CONSTANT_STRING:
+ case CONSTANT_NAMESPACE:
+ case CONSTANT_MULTINAME:
+ return new ValueWithIndex(index, valueType, null, "null", prefix);
+ }
+ }
+ switch(valueType) {
+ case CONSTANT_INT:
+ return new ValueWithIndex(index, valueType, abc.constants.getInt(index), "" + abc.constants.getInt(index), prefix);
+ case CONSTANT_UINT:
+ return new ValueWithIndex(index, valueType, abc.constants.getUInt(index), "" + abc.constants.getUInt(index), prefix);
+ case CONSTANT_DOUBLE:
+ return new ValueWithIndex(index, valueType, abc.constants.getDouble(index), EcmaScript.toString(abc.constants.getDouble(index)), prefix);
+ case CONSTANT_DECIMAL:
+ return new ValueWithIndex(index, valueType, abc.constants.getDecimal(index), "" + abc.constants.getDecimal(index), prefix);
+ case CONSTANT_FLOAT:
+ return new ValueWithIndex(index, valueType, abc.constants.getFloat(index), EcmaScript.toString(abc.constants.getFloat(index)), prefix);
+ case CONSTANT_FLOAT_4:
+ Float4 f4 = abc.constants.getFloat4(index);
+ return new ValueWithIndex(index, valueType, f4,
+ EcmaScript.toString(f4.values[0]) + " " +
+ EcmaScript.toString(f4.values[1]) + " " +
+ EcmaScript.toString(f4.values[2]) + " " +
+ EcmaScript.toString(f4.values[3]),
+ prefix
+ );
+ case CONSTANT_STRING:
+ return new ValueWithIndex(index, valueType, abc.constants.getString(index), "\"" + Helper.escapePCodeString(abc.constants.getString(index)) +"\"", prefix);
+ case CONSTANT_NAMESPACE:
+ return new ValueWithIndex(index, valueType, abc.constants.getNamespace(index), Multiname.namespaceToString(abc.constants, index), prefix);
+ case CONSTANT_NAMESPACE_SET:
+ return new ValueWithIndex(index, valueType, abc.constants.getNamespaceSet(index), Multiname.namespaceSetToString(abc.constants, index), prefix);
+ case CONSTANT_MULTINAME:
+ Multiname multiname = abc.constants.getMultiname(index);
+ return new ValueWithIndex(index, valueType, multiname, multiname.toString(abc.constants, new ArrayList()), prefix);
+ default:
+ return new ValueWithIndex(index, valueType, null, "", prefix);
+ }
+ }
+
+ @Override
+ public Object getChild(Object parent, int index) {
+ if (parent == type) {
+ return createValueWithIndex(index, type, "");
+ }
+ if (parent instanceof ValueWithIndex) {
+ ValueWithIndex vwi = (ValueWithIndex)parent;
+ if (vwi.value instanceof NamespaceSet) {
+ NamespaceSet nss = (NamespaceSet) vwi.value;
+ int ns = nss.namespaces[index];
+ return new ValueWithIndex(ns, TreeType.CONSTANT_NAMESPACE, abc.constants.getNamespace(ns), Multiname.namespaceToString(abc.constants, ns));
+ }
+ if (vwi.value instanceof Namespace) {
+ Namespace ns = (Namespace) vwi.value;
+ switch (index) {
+ case 0:
+ return "kind: " + Namespace.kindToStr(ns.kind);
+ case 1:
+ return createValueWithIndex(ns.name_index, TreeType.CONSTANT_STRING, "name: ");
+ }
+ }
+ if (vwi.value instanceof Multiname) {
+ Multiname m = (Multiname)vwi.value;
+ if (index == 0) {
+ return "kind: " + m.getKindStr();
+ }
+ int kind = m.kind;
+ if ((kind == Multiname.QNAME) || (kind == Multiname.QNAMEA)) {
+ switch (index) {
+ case 1:
+ return createValueWithIndex(m.namespace_index, TreeType.CONSTANT_NAMESPACE, "namespace: ");
+ case 2:
+ return createValueWithIndex(m.name_index, TreeType.CONSTANT_STRING, "name: ");
+ }
+ } else if ((kind == Multiname.RTQNAME) || (kind == Multiname.RTQNAMEA)) {
+ if (index == 1) {
+ return createValueWithIndex(m.name_index, TreeType.CONSTANT_STRING, "name: ");
+ }
+ } else if ((kind == Multiname.RTQNAMEL) || (kind == Multiname.RTQNAMELA)) {
+ //ignore
+ } else if ((kind == Multiname.MULTINAME) || (kind == Multiname.MULTINAMEA)) {
+ switch (index) {
+ case 1:
+ return createValueWithIndex(m.name_index, TreeType.CONSTANT_STRING, "name: ");
+ case 2:
+ return createValueWithIndex(m.namespace_set_index, TreeType.CONSTANT_NAMESPACE_SET, "namespace_set: ");
+ }
+ } else if ((kind == Multiname.MULTINAMEL) || (kind == Multiname.MULTINAMELA)) {
+ if (index == 1) {
+ return createValueWithIndex(m.namespace_set_index, TreeType.CONSTANT_NAMESPACE_SET, "namespace_set: ");
+ }
+ } else if (kind == Multiname.TYPENAME) {
+ if (index == 1) {
+ return createValueWithIndex(m.qname_index, TreeType.CONSTANT_MULTINAME, "qname: ");
+ }
+ if (index >= 2 && index - 2 < m.params.length) {
+ return createValueWithIndex(m.params[index - 2], TreeType.CONSTANT_MULTINAME, "param" + (index - 1) + ": ");
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public int getChildCount(Object parent) {
+ if (parent == type) {
+ switch(type) {
+ case CONSTANT_INT:
+ return abc.constants.getIntCount();
+ case CONSTANT_UINT:
+ return abc.constants.getUIntCount();
+ case CONSTANT_DOUBLE:
+ return abc.constants.getDoubleCount();
+ case CONSTANT_DECIMAL:
+ return abc.constants.getDecimalCount();
+ case CONSTANT_FLOAT:
+ return abc.constants.getFloatCount();
+ case CONSTANT_FLOAT_4:
+ return abc.constants.getFloat4Count();
+ case CONSTANT_STRING:
+ return abc.constants.getStringCount();
+ case CONSTANT_NAMESPACE:
+ return abc.constants.getNamespaceCount();
+ case CONSTANT_NAMESPACE_SET:
+ return abc.constants.getNamespaceSetCount();
+ case CONSTANT_MULTINAME:
+ return abc.constants.getMultinameCount();
+ case METHOD_INFO:
+ return abc.method_info.size();
+ case METADATA:
+ return abc.metadata_info.size();
+ case INSTANCE_INFO:
+ return abc.instance_info.size();
+ case CLASS_INFO:
+ return abc.class_info.size();
+ case SCRIPT_INFO:
+ return abc.script_info.size();
+ case METHOD_BODY:
+ return abc.bodies.size();
+ }
+ }
+ if (parent instanceof ValueWithIndex) {
+ ValueWithIndex vwi = (ValueWithIndex)parent;
+ if (vwi.value instanceof NamespaceSet) {
+ NamespaceSet nss = (NamespaceSet) vwi.value;
+ return nss.namespaces.length;
+ }
+ if (vwi.value instanceof Namespace) {
+ //kind, name
+ return 2;
+ }
+ if (vwi.value instanceof Multiname) {
+ Multiname m = (Multiname)vwi.value;
+ int kind = m.kind;
+ if ((kind == Multiname.QNAME) || (kind == Multiname.QNAMEA)) {
+ return 1 + 2;
+ } else if ((kind == Multiname.RTQNAME) || (kind == Multiname.RTQNAMEA)) {
+ return 1 + 1;
+ } else if ((kind == Multiname.RTQNAMEL) || (kind == Multiname.RTQNAMELA)) {
+ return 1;
+ } else if ((kind == Multiname.MULTINAME) || (kind == Multiname.MULTINAMEA)) {
+ return 1 + 2;
+ } else if ((kind == Multiname.MULTINAMEL) || (kind == Multiname.MULTINAMELA)) {
+ return 1 + 1;
+ } else if (kind == Multiname.TYPENAME) {
+ return 1 + 1 + m.params.length;
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean isLeaf(Object node) {
+ return getChildCount(node) == 0;
+ }
+
+ @Override
+ public void valueForPathChanged(TreePath path, Object newValue) {
+ }
+
+ @Override
+ public int getIndexOfChild(Object parent, Object child) {
+ if (child instanceof ValueWithIndex) {
+ ValueWithIndex vwi = (ValueWithIndex)child;
+ if (vwi.getType() == type) {
+ return vwi.index;
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ public void addTreeModelListener(TreeModelListener l) {
+ }
+
+ @Override
+ public void removeTreeModelListener(TreeModelListener l) {
+ }
+ }
+
+ public static class ExplorerTreeCellRenderer extends DefaultTreeCellRenderer {
+
+
+ public ExplorerTreeCellRenderer() {
+ setUI(new BasicLabelUI());
+ setOpaque(false);
+ if (View.isOceanic()) {
+ setBackgroundNonSelectionColor(Color.white);
+ }
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+
+ /*if (semiTransparent) {
+ if (getIcon() != null) {
+ Color color = getBackground();
+ Graphics2D g2d = (Graphics2D) g;
+ g2d.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha() / 2));
+ g2d.setComposite(AlphaComposite.SrcOver);
+ g2d.fillRect(0, 0, getWidth(), getHeight());
+ }
+ }*/
+ }
+
+ @Override
+ public Component getTreeCellRendererComponent(
+ JTree tree,
+ Object value,
+ boolean sel,
+ boolean expanded,
+ boolean leaf,
+ int row,
+ boolean hasFocus) {
+
+ super.getTreeCellRendererComponent(
+ tree, value, sel,
+ expanded, leaf, row,
+ hasFocus);
+
+ if (View.isOceanic()) {
+ setForeground(Color.BLACK);
+ }
+ setToolTipText(null);
+
+ //semitransparent = true;
+
+ return this;
+ }
+ }
+}
diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties
index fb332ce35..54721ee77 100644
--- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties
+++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties
@@ -1152,3 +1152,5 @@ button.export = Export
error.font.cannotaddcharacter = ERROR: Cannot add more characters to the font.\r\nSuccessfully added characters: %numchars%.
info.noteditable.compound = The script is compound - has multiple externally visible definitions. The direct editing is not available.
+
+menu.tools.abcexplorer = ABC Explorer
\ No newline at end of file
diff --git a/src/com/jpexs/decompiler/flash/gui/locales/abc/ABCExplorerDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/abc/ABCExplorerDialog.properties
new file mode 100644
index 000000000..6b75a2a31
--- /dev/null
+++ b/src/com/jpexs/decompiler/flash/gui/locales/abc/ABCExplorerDialog.properties
@@ -0,0 +1,18 @@
+# Copyright (C) 2023 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 .
+
+title = ABC Explorer
+abc = ABC:
+abc.info = %index% of %count%, v%major%.%minor%, %size%, Frame %frame%
\ No newline at end of file