mirror of
https://github.com/SuspiciousActivity/ByteEdit.git
synced 2026-08-13 17:56:17 +00:00
Cleanup
This commit is contained in:
1 parent
ea282d240e
commit
c2e22e55ad
7 files changed
+400
-346
No files matched your search
@@ -160,7 +160,7 @@ public class CompilationBox extends JFrame {
|
||||
}, ctrlS, JComponent.WHEN_FOCUSED);
|
||||
}
|
||||
|
||||
class JavaSourceFromString extends SimpleJavaFileObject {
|
||||
private static class JavaSourceFromString extends SimpleJavaFileObject {
|
||||
final String code;
|
||||
|
||||
public JavaSourceFromString(String name, String code) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.jetbrains.java.decompiler.main.Fernflower;
|
||||
@@ -60,8 +61,8 @@ public class FernflowerDecompiler implements IBytecodeProvider, IResultSaver, ID
|
||||
// TODO decompile method only
|
||||
this.bytes = b;
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
for (String key : options.keySet()) {
|
||||
map.put(key, options.get(key) ? "1" : "0");
|
||||
for (Entry<String, Boolean> entry : options.entrySet()) {
|
||||
map.put(entry.getKey(), entry.getValue() ? "1" : "0");
|
||||
}
|
||||
Fernflower f = new Fernflower(this, this, map, new IFernflowerLogger() {
|
||||
@Override
|
||||
|
||||
@@ -494,11 +494,13 @@ public class Assembler {
|
||||
}
|
||||
return clazz;
|
||||
} catch (Throwable e) {
|
||||
System.err.println("Error at line: " + read.currentLine);
|
||||
try {
|
||||
Main.txtByteEditView.setCaretPosition(Main.txtByteEditView.getLineStartOffset(read.currentLine));
|
||||
} catch (BadLocationException e1) {
|
||||
System.err.println("Can't show line!");
|
||||
if (read != null) {
|
||||
System.err.println("Error at line: " + read.currentLine);
|
||||
try {
|
||||
Main.txtByteEditView.setCaretPosition(Main.txtByteEditView.getLineStartOffset(read.currentLine));
|
||||
} catch (BadLocationException e1) {
|
||||
System.err.println("Can't show line!");
|
||||
}
|
||||
}
|
||||
Main.showError(e.getMessage());
|
||||
e.printStackTrace();
|
||||
@@ -599,26 +601,28 @@ public class Assembler {
|
||||
if (args.length == 0) {
|
||||
return null;
|
||||
}
|
||||
String ret = args[start];
|
||||
StringBuilder sb = new StringBuilder(args[start]);
|
||||
if (args.length > start + 1) {
|
||||
for (int i = start + 1; i < args.length; i++) {
|
||||
ret = ret + " " + args[i];
|
||||
sb.append(' ');
|
||||
sb.append(args[i]);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String consolidateStrings(String[] args, int start, int end) {
|
||||
if (end == 0) {
|
||||
return null;
|
||||
}
|
||||
String ret = args[start];
|
||||
StringBuilder sb = new StringBuilder(args[start]);
|
||||
if (end > start + 1) {
|
||||
for (int i = start + 1; i < end; i++) {
|
||||
ret = ret + " " + args[i];
|
||||
sb.append(' ');
|
||||
sb.append(args[i]);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static Object getValue(HugeStringsRev hsr, String s, String to) {
|
||||
@@ -1567,7 +1571,7 @@ public class Assembler {
|
||||
if (!asd.startsWith("(")) {
|
||||
int frameType = ClassUtil.getFrameTypeByName(asd);
|
||||
if (frameType != -1)
|
||||
list.add(new Integer(frameType));
|
||||
list.add(Integer.valueOf(frameType));
|
||||
else
|
||||
list.add(UnicodeUtils.unescape(hsr, asd.substring(1, asd.length() - 1)));
|
||||
} else {
|
||||
@@ -1590,13 +1594,7 @@ public class Assembler {
|
||||
}
|
||||
}
|
||||
}
|
||||
Object[] arr = new Object[list.size()];
|
||||
int c = 0;
|
||||
for (Object o : list) {
|
||||
arr[c] = o;
|
||||
c++;
|
||||
}
|
||||
return arr;
|
||||
return list.toArray(new Object[list.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
package me.ByteEdit.main;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
public class ArchiveTreeModel extends DefaultTreeModel {
|
||||
|
||||
public final ArrayList<String> paths = new ArrayList<>();
|
||||
private final HashMap<String, ClassNode> classNodes;
|
||||
private final HashMap<String, byte[]> otherFiles;
|
||||
private final ZipFile jar;
|
||||
|
||||
public boolean newCreated = false;
|
||||
|
||||
public ArchiveTreeModel(HashMap<String, ClassNode> classNodes, HashMap<String, byte[]> otherFiles) {
|
||||
super(new ByteEditTreeNode("New"));
|
||||
this.newCreated = true;
|
||||
this.jar = null;
|
||||
this.classNodes = classNodes;
|
||||
this.otherFiles = otherFiles;
|
||||
}
|
||||
|
||||
public ArchiveTreeModel(ZipFile jar, HashMap<String, ClassNode> classNodes, HashMap<String, byte[]> otherFiles) {
|
||||
super(new ByteEditTreeNode(jar.getName().split(File.separator.equals("\\") ? "\\\\"
|
||||
: File.separator)[jar.getName().split(File.separator.equals("\\") ? "\\\\" : File.separator).length
|
||||
- 1]));
|
||||
this.newCreated = true;
|
||||
this.jar = jar;
|
||||
this.classNodes = classNodes;
|
||||
this.otherFiles = otherFiles;
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
try {
|
||||
classNodes.clear();
|
||||
otherFiles.clear();
|
||||
Enumeration<? extends ZipEntry> enumeration = jar.entries();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
ZipEntry next = enumeration.nextElement();
|
||||
byte[] data = toByteArray(jar.getInputStream(next));
|
||||
if (next.getSize() != 0 && !next.getName().startsWith("META-INF")
|
||||
&& (next.getName().endsWith(".class") || next.getName().endsWith(".class/"))) {
|
||||
try {
|
||||
ClassReader reader = new ClassReader(data);
|
||||
ClassNode node = new ClassNode();
|
||||
reader.accept(node, 0);
|
||||
classNodes.put(Main.getFullName(node.name), node);
|
||||
|
||||
if ((next.getName().contains("/")
|
||||
? (!Main.patternSlash
|
||||
.split(next.getName())[Main.patternSlash.split(next.getName()).length - 1]
|
||||
.contains("$"))
|
||||
: (!next.getName().contains("$")))
|
||||
|| (next.getName().startsWith("$") || next.getName().contains("$$")
|
||||
|| next.getName().endsWith("$"))) {
|
||||
paths.add(Main.getFullName(node.name));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
otherFiles.put(next.getName(), data);
|
||||
}
|
||||
} else {
|
||||
otherFiles.put(next.getName(), data);
|
||||
}
|
||||
}
|
||||
jar.close();
|
||||
refresh();
|
||||
} catch (Throwable e) {
|
||||
classNodes.clear();
|
||||
otherFiles.clear();
|
||||
e.printStackTrace();
|
||||
Main.showError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private int getSlashCount(String s) {
|
||||
int count = 0;
|
||||
for (char c : s.toCharArray()) {
|
||||
if (c == '/')
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
setRoot(new ByteEditTreeNode(((ByteEditTreeNode) getRoot()).toString()));
|
||||
Collections.sort(paths, String.CASE_INSENSITIVE_ORDER);
|
||||
Collections.sort(paths, new Comparator<String>() {
|
||||
public int compare(String s1, String s2) {
|
||||
return getSlashCount(s2) - getSlashCount(s1);
|
||||
}
|
||||
});
|
||||
|
||||
// Windows-like sorting (like file explorer)
|
||||
List<String> tmp = new ArrayList<>(paths);
|
||||
|
||||
List<String> folders = tmp.stream().filter(s -> {
|
||||
int c = countSlashes(s, 50);
|
||||
return c > 0 && c < 50;
|
||||
}).map(s -> {
|
||||
int idx = s.lastIndexOf('/');
|
||||
if (idx == s.length() - 1)
|
||||
idx = s.lastIndexOf('/', idx - 1);
|
||||
if (idx == -1)
|
||||
return s;
|
||||
return s.substring(0, idx);
|
||||
}).distinct().sorted(new Comparator<String>() {
|
||||
@Override
|
||||
public int compare(String s, String s2) {
|
||||
// From String#compareToIgnoreCase(String)
|
||||
int n = s.length();
|
||||
int n2 = s2.length();
|
||||
int n3 = Math.min(n, n2);
|
||||
for (int i = 0; i < n3; ++i) {
|
||||
char c;
|
||||
char c2 = s.charAt(i);
|
||||
if (c2 == (c = s2.charAt(i)) || (c2 = Character.toUpperCase(c2)) == (c = Character.toUpperCase(c))
|
||||
|| (c2 = Character.toLowerCase(c2)) == (c = Character.toLowerCase(c)))
|
||||
continue;
|
||||
return c2 - c;
|
||||
}
|
||||
// Other way around
|
||||
return n2 - n;
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
tmp.removeAll(folders);
|
||||
tmp.addAll(0, folders);
|
||||
|
||||
for (String s : tmp) {
|
||||
String[] elements = Main.patternSlash.split(s, 50);
|
||||
if (elements.length >= 50) {
|
||||
((ByteEditTreeNode) getRoot()).add(new ByteEditTreeNode(s, s));
|
||||
} else {
|
||||
ByteEditTreeNode currentNode = (ByteEditTreeNode) getRoot();
|
||||
for (int i = 0; i < elements.length; i++) {
|
||||
String token = elements[i];
|
||||
ByteEditTreeNode nextNode = findNode(currentNode, token);
|
||||
if (nextNode == null) {
|
||||
nextNode = new ByteEditTreeNode(token, s);
|
||||
currentNode.add(nextNode);
|
||||
}
|
||||
currentNode = nextNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ByteEditTreeNode findNode(ByteEditTreeNode parent, String name) {
|
||||
Enumeration<?> e = parent.children();
|
||||
while (e.hasMoreElements()) {
|
||||
ByteEditTreeNode element = (ByteEditTreeNode) e.nextElement();
|
||||
if (element.getUserObject().equals(name)) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int countSlashes(String s, int max) {
|
||||
int count = 0;
|
||||
for (char c : s.toCharArray()) {
|
||||
if (c == '/') {
|
||||
count++;
|
||||
if (count == max)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static byte[] toByteArray(final InputStream input) throws IOException {
|
||||
try (final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int read;
|
||||
while ((read = input.read(buffer)) != -1) {
|
||||
output.write(buffer, 0, read);
|
||||
}
|
||||
return output.toByteArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package me.ByteEdit.main;
|
||||
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.event.TreeExpansionEvent;
|
||||
import javax.swing.event.TreeExpansionListener;
|
||||
import javax.swing.tree.TreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
public class FurtherExpandingTreeExpansionListener implements TreeExpansionListener {
|
||||
|
||||
private final JTree tree;
|
||||
|
||||
public FurtherExpandingTreeExpansionListener(JTree tree) {
|
||||
this.tree = tree;
|
||||
}
|
||||
|
||||
public void treeExpanded(TreeExpansionEvent event) {
|
||||
TreePath treePath = event.getPath();
|
||||
Object expandedTreePathObject = treePath.getLastPathComponent();
|
||||
if (!(expandedTreePathObject instanceof TreeNode))
|
||||
return;
|
||||
TreeNode expandedTreeNode = (TreeNode) expandedTreePathObject;
|
||||
if (expandedTreeNode.getChildCount() == 1) {
|
||||
TreeNode descendantTreeNode = expandedTreeNode.getChildAt(0);
|
||||
if (descendantTreeNode.isLeaf())
|
||||
return;
|
||||
TreePath nextTreePath = treePath.pathByAddingChild(descendantTreeNode);
|
||||
tree.expandPath(nextTreePath);
|
||||
}
|
||||
}
|
||||
|
||||
public void treeCollapsed(TreeExpansionEvent event) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package me.ByteEdit.main;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rtextarea.RTextArea;
|
||||
import org.fife.ui.rtextarea.ToolTipSupplier;
|
||||
|
||||
import me.ByteEdit.edit.Assembler;
|
||||
|
||||
public class HugeStringsToolTips implements ToolTipSupplier {
|
||||
|
||||
private final RSyntaxTextArea txtByteEditView;
|
||||
|
||||
public HugeStringsToolTips(RSyntaxTextArea txtByteEditView) {
|
||||
this.txtByteEditView = txtByteEditView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText(RTextArea textArea, MouseEvent e) {
|
||||
int idx = textArea.viewToModel(e.getPoint());
|
||||
if (idx < 0)
|
||||
return null;
|
||||
int subIdx = Math.max(idx - 15, 0);
|
||||
int subLen = Math.min(30, textArea.getDocument().getLength() - subIdx);
|
||||
if (subLen <= 0)
|
||||
return null;
|
||||
try {
|
||||
String cut = textArea.getText(subIdx, subLen);
|
||||
int hashIdx = -1;
|
||||
for (int off = idx - subIdx; off >= 0; off--) {
|
||||
if (cut.charAt(off) == '#') {
|
||||
hashIdx = off;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hashIdx < 0) {
|
||||
for (int off = idx - subIdx; off < cut.length(); off++) {
|
||||
if (cut.charAt(off) == '#') {
|
||||
hashIdx = off;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hashIdx < 0)
|
||||
return null;
|
||||
if (hashIdx >= 5 && cut.substring(hashIdx - 5, hashIdx).equals("ldc \"")) // ldc "#..."
|
||||
return null;
|
||||
int numLen = 1;
|
||||
for (int i = hashIdx + 1; i < cut.length(); i++) {
|
||||
if (!(cut.charAt(i) - '0' >= 0 && cut.charAt(i) - '0' <= 9)) {
|
||||
break;
|
||||
}
|
||||
numLen++;
|
||||
}
|
||||
if (numLen < 2 || hashIdx > idx - subIdx || idx - subIdx - numLen >= hashIdx)
|
||||
return null;
|
||||
String hashNum = cut.substring(hashIdx, hashIdx + numLen);
|
||||
|
||||
String txt = txtByteEditView.getText();
|
||||
int len = 0;
|
||||
for (String asm : txt.split("\\/\\/ #Annotations:\n")) {
|
||||
len += 19; // "\\/\\/ #Annotations:\n".length()
|
||||
if (asm.isEmpty())
|
||||
continue;
|
||||
if (idx >= len && idx < len + asm.length()) {
|
||||
try (BufferedReader reader = new BufferedReader(new StringReader(asm))) {
|
||||
String s;
|
||||
while (!(s = reader.readLine()).startsWith("// #SourceFile: ")) {
|
||||
}
|
||||
while (!(s = reader.readLine()).equals("// #Fields")) {
|
||||
if (s.startsWith("#")) {
|
||||
String[] split = Assembler.SPACE.split(s, 2);
|
||||
if (hashNum.equals(split[0].substring(0, split[0].length() - 1))) {
|
||||
String toolTip = split[1];
|
||||
if (toolTip.length() > 500)
|
||||
toolTip = toolTip.substring(0, 500) + "...";
|
||||
return toolTip;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
len += asm.length();
|
||||
}
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+50
-324
@@ -20,26 +20,17 @@ import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
@@ -63,16 +54,12 @@ import javax.swing.JSplitPane;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.TreeExpansionEvent;
|
||||
import javax.swing.event.TreeExpansionListener;
|
||||
import javax.swing.event.TreeSelectionEvent;
|
||||
import javax.swing.event.TreeSelectionListener;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import javax.swing.tree.TreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
import javax.swing.tree.TreeSelectionModel;
|
||||
|
||||
import org.fife.ui.autocomplete.AutoCompletion;
|
||||
@@ -84,10 +71,7 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
|
||||
import org.fife.ui.rsyntaxtextarea.folding.CurlyFoldParser;
|
||||
import org.fife.ui.rsyntaxtextarea.folding.FoldParserManager;
|
||||
import org.fife.ui.rtextarea.RTextArea;
|
||||
import org.fife.ui.rtextarea.RTextScrollPane;
|
||||
import org.fife.ui.rtextarea.ToolTipSupplier;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
@@ -110,11 +94,18 @@ import me.ByteEdit.utils.UnicodeUtils;
|
||||
public class Main extends JFrame {
|
||||
|
||||
public static Main INSTANCE;
|
||||
|
||||
public static final Pattern patternSlash = Pattern.compile("/");
|
||||
private static final Pattern patternRenameableField = Pattern
|
||||
.compile("^\t(?:[a-z]+ |0x[0-9a-fA-F]+ )*?(\\[*(?:V|Z|C|B|S|I|F|J|D|L.+?;)) ([^ ]+) ?.*");
|
||||
private static final Pattern patternRenameableMethod = Pattern
|
||||
.compile("^\t(?:[a-z]+ |0x[0-9a-fA-F]+ )*?([^ ]+) (\\((?:\\[*(?:V|Z|C|B|S|I|F|J|D|L.+?;))*\\)[^ ]+) .*\\{");
|
||||
|
||||
private JPanel contentPane;
|
||||
private volatile boolean isChangingFile;
|
||||
public static File jarFile;
|
||||
public static HashMap<String, byte[]> OTHER_FILES = new HashMap<>();
|
||||
public static HashMap<String, ClassNode> classNodes = new HashMap<>();
|
||||
public static final HashMap<String, ClassNode> classNodes = new HashMap<>();
|
||||
public static final HashMap<String, byte[]> otherFiles = new HashMap<>();
|
||||
public static String currentNodeName;
|
||||
public static RSyntaxTextArea txtByteEditView;
|
||||
public static GlobalSearchBox globalSearchBox;
|
||||
@@ -128,7 +119,6 @@ public class Main extends JFrame {
|
||||
public static RTextScrollPane scrollPane_ByteEdit;
|
||||
public static File saveFolder;
|
||||
|
||||
private static final Pattern SLASH = Pattern.compile("/");
|
||||
private JMenuBar menuBar;
|
||||
private JMenu mnFile;
|
||||
private JMenuItem mntmOpenJar;
|
||||
@@ -158,6 +148,8 @@ public class Main extends JFrame {
|
||||
|
||||
public static final Object treeLock = new Object();
|
||||
|
||||
public static EnumDecompiler decompiler = EnumDecompiler.BYTEEDIT;
|
||||
|
||||
/**
|
||||
* Launch the application.
|
||||
*/
|
||||
@@ -312,7 +304,8 @@ public class Main extends JFrame {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
ArchiveTreeModel model = new ArchiveTreeModel(new ZipFile(jarFile));
|
||||
ArchiveTreeModel model = new ArchiveTreeModel(new ZipFile(jarFile), classNodes,
|
||||
otherFiles);
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
synchronized (treeLock) {
|
||||
@@ -451,7 +444,7 @@ public class Main extends JFrame {
|
||||
new CurlyFoldParser(false, true));
|
||||
tree = new JTree(new DefaultTreeModel(null));
|
||||
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
||||
tree.addTreeExpansionListener(new FurtherExpandingTreeExpansionListener());
|
||||
tree.addTreeExpansionListener(new FurtherExpandingTreeExpansionListener(tree));
|
||||
tree.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
@@ -466,7 +459,8 @@ public class Main extends JFrame {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
ArchiveTreeModel model = new ArchiveTreeModel(new ZipFile(jarFile));
|
||||
ArchiveTreeModel model = new ArchiveTreeModel(new ZipFile(jarFile), classNodes,
|
||||
otherFiles);
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
synchronized (treeLock) {
|
||||
@@ -561,7 +555,8 @@ public class Main extends JFrame {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
ArchiveTreeModel model = new ArchiveTreeModel(new ZipFile(jarFile));
|
||||
ArchiveTreeModel model = new ArchiveTreeModel(new ZipFile(jarFile),
|
||||
classNodes, otherFiles);
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
synchronized (treeLock) {
|
||||
@@ -618,84 +613,7 @@ public class Main extends JFrame {
|
||||
txtByteEditView = new RSyntaxTextArea();
|
||||
ac.install(txtByteEditView);
|
||||
txtByteEditView.setCodeFoldingEnabled(true);
|
||||
// ToolTips for HugeStrings
|
||||
txtByteEditView.setToolTipSupplier(new ToolTipSupplier() {
|
||||
|
||||
@Override
|
||||
public String getToolTipText(RTextArea textArea, MouseEvent e) {
|
||||
int idx = textArea.viewToModel(e.getPoint());
|
||||
if (idx < 0)
|
||||
return null;
|
||||
int subIdx = Math.max(idx - 15, 0);
|
||||
int subLen = Math.min(30, textArea.getDocument().getLength() - subIdx);
|
||||
if (subLen <= 0)
|
||||
return null;
|
||||
try {
|
||||
String cut = textArea.getText(subIdx, subLen);
|
||||
int hashIdx = -1;
|
||||
for (int off = idx - subIdx; off >= 0; off--) {
|
||||
if (cut.charAt(off) == '#') {
|
||||
hashIdx = off;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hashIdx < 0) {
|
||||
for (int off = idx - subIdx; off < cut.length(); off++) {
|
||||
if (cut.charAt(off) == '#') {
|
||||
hashIdx = off;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hashIdx < 0)
|
||||
return null;
|
||||
if (hashIdx >= 5 && cut.substring(hashIdx - 5, hashIdx).equals("ldc \"")) // ldc "#..."
|
||||
return null;
|
||||
int numLen = 1;
|
||||
for (int i = hashIdx + 1; i < cut.length(); i++) {
|
||||
if (!(cut.charAt(i) - '0' >= 0 && cut.charAt(i) - '0' <= 9)) {
|
||||
break;
|
||||
}
|
||||
numLen++;
|
||||
}
|
||||
if (numLen < 2 || hashIdx > idx - subIdx || idx - subIdx - numLen >= hashIdx)
|
||||
return null;
|
||||
String hashNum = cut.substring(hashIdx, hashIdx + numLen);
|
||||
|
||||
String txt = txtByteEditView.getText();
|
||||
int len = 0;
|
||||
for (String asm : txt.split("\\/\\/ #Annotations:\n")) {
|
||||
len += 19; // "\\/\\/ #Annotations:\n".length()
|
||||
if (asm.isEmpty())
|
||||
continue;
|
||||
if (idx >= len && idx < len + asm.length()) {
|
||||
try (BufferedReader reader = new BufferedReader(new StringReader(asm))) {
|
||||
String s;
|
||||
while (!(s = reader.readLine()).startsWith("// #SourceFile: ")) {
|
||||
}
|
||||
while (!(s = reader.readLine()).equals("// #Fields")) {
|
||||
if (s.startsWith("#")) {
|
||||
String[] split = Assembler.SPACE.split(s, 2);
|
||||
if (hashNum.equals(split[0].substring(0, split[0].length() - 1))) {
|
||||
String toolTip = split[1];
|
||||
if (toolTip.length() > 500)
|
||||
toolTip = toolTip.substring(0, 500) + "...";
|
||||
return toolTip;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
len += asm.length();
|
||||
}
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
txtByteEditView.setToolTipSupplier(new HugeStringsToolTips(txtByteEditView));
|
||||
txtByteEditView.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
@@ -737,24 +655,18 @@ public class Main extends JFrame {
|
||||
// specific
|
||||
tree.registerKeyboardAction(e -> save(), ctrlS, JComponent.WHEN_FOCUSED);
|
||||
txtByteEditView.registerKeyboardAction(e -> saveCurrentClassNode(), ctrlS, JComponent.WHEN_FOCUSED);
|
||||
txtByteEditView.registerKeyboardAction(e -> {
|
||||
try {
|
||||
renameSelected();
|
||||
} catch (BadLocationException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}, ctrlR, JComponent.WHEN_FOCUSED);
|
||||
txtByteEditView.registerKeyboardAction(e -> renameSelected(), ctrlR, JComponent.WHEN_FOCUSED);
|
||||
txtByteEditView.setEditable(true);
|
||||
txtByteEditView.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA_DISASSEMBLE);
|
||||
txtByteEditView.setCodeFoldingEnabled(true);
|
||||
scrollPane_ByteEdit = new RTextScrollPane();
|
||||
ThemeManager.registerTextArea(txtByteEditView);
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.add(scrollPane_ByteEdit, BorderLayout.CENTER);
|
||||
final JComboBox<String> decompilerMode = new JComboBox<>();
|
||||
DefaultComboBoxModel model = new DefaultComboBoxModel();
|
||||
Arrays.stream(EnumDecompiler.values()).forEach(model::addElement);
|
||||
Stream.of(EnumDecompiler.values()).forEach(model::addElement);
|
||||
decompilerMode.setModel(model);
|
||||
decompilerMode.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
@@ -790,30 +702,27 @@ public class Main extends JFrame {
|
||||
scrollPane_ByteEdit.setFoldIndicatorEnabled(true);
|
||||
}
|
||||
|
||||
public static EnumDecompiler decompiler = EnumDecompiler.BYTEEDIT;
|
||||
|
||||
private final Pattern renameableFieldPattern = Pattern
|
||||
.compile("^\t(?:[a-z]+ |0x[0-9a-fA-F]+ )*?(\\[*(?:V|Z|C|B|S|I|F|J|D|L.+?;)) ([^ ]+) ?.*");
|
||||
private final Pattern renameableMethodPattern = Pattern
|
||||
.compile("^\t(?:[a-z]+ |0x[0-9a-fA-F]+ )*?([^ ]+) (\\((?:\\[*(?:V|Z|C|B|S|I|F|J|D|L.+?;))*\\)[^ ]+) .*\\{");
|
||||
|
||||
public void renameSelected() throws BadLocationException {
|
||||
int lineStart = txtByteEditView.getLineStartOffsetOfCurrentLine();
|
||||
int lineEnd = txtByteEditView.getLineEndOffsetOfCurrentLine() - 1;
|
||||
String line = txtByteEditView.getText(lineStart, lineEnd - lineStart);
|
||||
Matcher m = renameableFieldPattern.matcher(line);
|
||||
if (m.find()) {
|
||||
setRenameInfo(m, lineEnd);
|
||||
} else {
|
||||
m = renameableMethodPattern.matcher(line);
|
||||
public void renameSelected() {
|
||||
try {
|
||||
int lineStart = txtByteEditView.getLineStartOffsetOfCurrentLine();
|
||||
int lineEnd = txtByteEditView.getLineEndOffsetOfCurrentLine() - 1;
|
||||
String line = txtByteEditView.getText(lineStart, lineEnd - lineStart);
|
||||
Matcher m = patternRenameableField.matcher(line);
|
||||
if (m.find()) {
|
||||
if (ClassUtil.isObjectClassMethod(m.group(1), m.group(2), false)) {
|
||||
JOptionPane.showMessageDialog(null, "This method can not be renamed!", "Error!",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
setRenameInfo(m, lineEnd);
|
||||
} else {
|
||||
m = patternRenameableMethod.matcher(line);
|
||||
if (m.find()) {
|
||||
if (ClassUtil.isObjectClassMethod(m.group(1), m.group(2), false)) {
|
||||
JOptionPane.showMessageDialog(null, "This method can not be renamed!", "Error!",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
setRenameInfo(m, lineEnd);
|
||||
}
|
||||
}
|
||||
} catch (BadLocationException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -838,9 +747,9 @@ public class Main extends JFrame {
|
||||
String[] split = s.split(" ");
|
||||
String className = UnicodeUtils.unescape(null, split[split.length - 1], true);
|
||||
renameBox.className = className;
|
||||
String name = UnicodeUtils.unescape(null, m.pattern() == renameableFieldPattern ? m.group(2) : m.group(1),
|
||||
String name = UnicodeUtils.unescape(null, m.pattern() == patternRenameableField ? m.group(2) : m.group(1),
|
||||
true);
|
||||
String desc = UnicodeUtils.unescape(null, m.pattern() == renameableFieldPattern ? m.group(1) : m.group(2),
|
||||
String desc = UnicodeUtils.unescape(null, m.pattern() == patternRenameableField ? m.group(1) : m.group(2),
|
||||
true);
|
||||
renameBox.name = name;
|
||||
renameBox.desc = desc;
|
||||
@@ -864,7 +773,7 @@ public class Main extends JFrame {
|
||||
classNodes.put(getFullName(node.name), node);
|
||||
ArchiveTreeModel model;
|
||||
if (tree.getModel().getClass().equals(DefaultTreeModel.class)) {
|
||||
tree.setModel(model = new ArchiveTreeModel());
|
||||
tree.setModel(model = new ArchiveTreeModel(classNodes, otherFiles));
|
||||
} else {
|
||||
model = (ArchiveTreeModel) tree.getModel();
|
||||
}
|
||||
@@ -890,11 +799,11 @@ public class Main extends JFrame {
|
||||
String dis = decompiler.getDecompiler().decompile(classNode);
|
||||
String substr = current.substring(0, current.length() - 6);
|
||||
synchronized (Main.classNodes) {
|
||||
for (String key : Main.classNodes.keySet()) {
|
||||
if (key.contains("$")) {
|
||||
String[] split = key.split("\\$");
|
||||
for (Entry<String, ClassNode> entry : Main.classNodes.entrySet()) {
|
||||
if (entry.getKey().contains("$")) {
|
||||
String[] split = entry.getKey().split("\\$");
|
||||
if (split[0].equals(substr)) {
|
||||
dis += "\n" + decompiler.getDecompiler().decompile(classNodes.get(key));
|
||||
dis += "\n" + decompiler.getDecompiler().decompile(entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1000,12 +909,12 @@ public class Main extends JFrame {
|
||||
if (name != null && !name.isEmpty()) {
|
||||
String val = "Manifest-Version: 1.0\n" + "Class-Path: .\n" + "Main-Class: " + name.replace('/', '.')
|
||||
+ "\n\n";
|
||||
OTHER_FILES.put("META-INF/MANIFEST.MF", val.getBytes());
|
||||
otherFiles.put("META-INF/MANIFEST.MF", val.getBytes());
|
||||
}
|
||||
if (tree.getModel() instanceof ArchiveTreeModel)
|
||||
((ArchiveTreeModel) tree.getModel()).newCreated = false;
|
||||
}
|
||||
for (Entry<String, byte[]> entry : OTHER_FILES.entrySet()) {
|
||||
for (Entry<String, byte[]> entry : otherFiles.entrySet()) {
|
||||
ZipEntry ent = new ZipEntry(entry.getKey());
|
||||
output.putNextEntry(ent);
|
||||
output.write(entry.getValue());
|
||||
@@ -1051,187 +960,4 @@ public class Main extends JFrame {
|
||||
public static String getFullName(String s) {
|
||||
return s + ".class";
|
||||
}
|
||||
|
||||
class ArchiveTreeModel extends DefaultTreeModel {
|
||||
|
||||
public ArrayList<String> paths = new ArrayList<>();
|
||||
public boolean newCreated = false;
|
||||
|
||||
public ArchiveTreeModel() {
|
||||
super(new ByteEditTreeNode("New"));
|
||||
newCreated = true;
|
||||
}
|
||||
|
||||
public ArchiveTreeModel(ZipFile jar) {
|
||||
super(new ByteEditTreeNode(jar.getName().split(File.separator.equals("\\") ? "\\\\"
|
||||
: File.separator)[jar.getName().split(File.separator.equals("\\") ? "\\\\" : File.separator).length
|
||||
- 1]));
|
||||
try {
|
||||
classNodes.clear();
|
||||
OTHER_FILES.clear();
|
||||
Enumeration<? extends ZipEntry> enumeration = jar.entries();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
ZipEntry next = enumeration.nextElement();
|
||||
byte[] data = toByteArray(jar.getInputStream(next));
|
||||
if (next.getSize() != 0 && !next.getName().startsWith("META-INF")
|
||||
&& (next.getName().endsWith(".class") || next.getName().endsWith(".class/"))) {
|
||||
try {
|
||||
ClassReader reader = new ClassReader(data);
|
||||
ClassNode node = new ClassNode();
|
||||
reader.accept(node, 0);
|
||||
classNodes.put(getFullName(node.name), node);
|
||||
|
||||
if ((next.getName().contains("/")
|
||||
? (!SLASH.split(next.getName())[SLASH.split(next.getName()).length - 1]
|
||||
.contains("$"))
|
||||
: (!next.getName().contains("$")))
|
||||
|| (next.getName().startsWith("$") || next.getName().contains("$$")
|
||||
|| next.getName().endsWith("$"))) {
|
||||
paths.add(getFullName(node.name));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
OTHER_FILES.put(next.getName(), data);
|
||||
}
|
||||
} else {
|
||||
OTHER_FILES.put(next.getName(), data);
|
||||
}
|
||||
}
|
||||
jar.close();
|
||||
refresh();
|
||||
} catch (Throwable e) {
|
||||
classNodes.clear();
|
||||
OTHER_FILES.clear();
|
||||
e.printStackTrace();
|
||||
showError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private int getSlashCount(String s) {
|
||||
int count = 0;
|
||||
for (char c : s.toCharArray()) {
|
||||
if (c == '/')
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
setRoot(new ByteEditTreeNode(((ByteEditTreeNode) getRoot()).toString()));
|
||||
Collections.sort(paths, String.CASE_INSENSITIVE_ORDER);
|
||||
Collections.sort(paths, new Comparator<String>() {
|
||||
public int compare(String s1, String s2) {
|
||||
return getSlashCount(s2) - getSlashCount(s1);
|
||||
}
|
||||
});
|
||||
|
||||
// Windows-like sorting (like file explorer)
|
||||
List<String> tmp = new ArrayList<>(paths);
|
||||
|
||||
List<String> folders = tmp.stream().filter(s -> {
|
||||
int c = countSlashes(s, 50);
|
||||
return c > 0 && c < 50;
|
||||
}).map(s -> {
|
||||
int idx = s.lastIndexOf('/');
|
||||
if (idx == s.length() - 1)
|
||||
idx = s.lastIndexOf('/', idx - 1);
|
||||
if (idx == -1)
|
||||
return s;
|
||||
return s.substring(0, idx);
|
||||
}).distinct().sorted(new Comparator<String>() {
|
||||
@Override
|
||||
public int compare(String s, String s2) {
|
||||
// From String#compareToIgnoreCase(String)
|
||||
int n = s.length();
|
||||
int n2 = s2.length();
|
||||
int n3 = Math.min(n, n2);
|
||||
for (int i = 0; i < n3; ++i) {
|
||||
char c;
|
||||
char c2 = s.charAt(i);
|
||||
if (c2 == (c = s2.charAt(i))
|
||||
|| (c2 = Character.toUpperCase(c2)) == (c = Character.toUpperCase(c))
|
||||
|| (c2 = Character.toLowerCase(c2)) == (c = Character.toLowerCase(c)))
|
||||
continue;
|
||||
return c2 - c;
|
||||
}
|
||||
// Other way around
|
||||
return n2 - n;
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
tmp.removeAll(folders);
|
||||
tmp.addAll(0, folders);
|
||||
|
||||
for (String s : tmp) {
|
||||
String[] elements = SLASH.split(s, 50);
|
||||
if (elements.length >= 50) {
|
||||
((ByteEditTreeNode) getRoot()).add(new ByteEditTreeNode(s, s));
|
||||
} else {
|
||||
ByteEditTreeNode currentNode = (ByteEditTreeNode) getRoot();
|
||||
for (int i = 0; i < elements.length; i++) {
|
||||
String token = elements[i];
|
||||
ByteEditTreeNode nextNode = findNode(currentNode, token);
|
||||
if (nextNode == null) {
|
||||
nextNode = new ByteEditTreeNode(token, s);
|
||||
currentNode.add(nextNode);
|
||||
}
|
||||
currentNode = nextNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ByteEditTreeNode findNode(ByteEditTreeNode parent, String name) {
|
||||
Enumeration<?> e = parent.children();
|
||||
while (e.hasMoreElements()) {
|
||||
ByteEditTreeNode element = (ByteEditTreeNode) e.nextElement();
|
||||
if (element.getUserObject().equals(name)) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private int countSlashes(String s, int max) {
|
||||
int count = 0;
|
||||
for (char c : s.toCharArray()) {
|
||||
if (c == '/') {
|
||||
count++;
|
||||
if (count == max)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FurtherExpandingTreeExpansionListener implements TreeExpansionListener {
|
||||
public void treeExpanded(TreeExpansionEvent event) {
|
||||
TreePath treePath = event.getPath();
|
||||
Object expandedTreePathObject = treePath.getLastPathComponent();
|
||||
if (!(expandedTreePathObject instanceof TreeNode))
|
||||
return;
|
||||
TreeNode expandedTreeNode = (TreeNode) expandedTreePathObject;
|
||||
if (expandedTreeNode.getChildCount() == 1) {
|
||||
TreeNode descendantTreeNode = expandedTreeNode.getChildAt(0);
|
||||
if (descendantTreeNode.isLeaf())
|
||||
return;
|
||||
TreePath nextTreePath = treePath.pathByAddingChild(descendantTreeNode);
|
||||
tree.expandPath(nextTreePath);
|
||||
}
|
||||
}
|
||||
|
||||
public void treeCollapsed(TreeExpansionEvent event) {
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] toByteArray(final InputStream input) throws IOException {
|
||||
try (final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int read;
|
||||
while ((read = input.read(buffer)) != -1) {
|
||||
output.write(buffer, 0, read);
|
||||
}
|
||||
return output.toByteArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user