diff --git a/README.md b/README.md index 353cff0..2603ea9 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,5 @@ To open a class, press CTRL + T. To change settings like auto-computation of stack frames and max locals/stack press CTRL + O. To go to the method definition of the selected line's invoke-method press CTRL + G. + +To (un-)escape unicode characters, press CTRL + U. diff --git a/src/me/ByteEdit/boxes/OptionBox.java b/src/me/ByteEdit/boxes/OptionBox.java new file mode 100644 index 0000000..df2c4e3 --- /dev/null +++ b/src/me/ByteEdit/boxes/OptionBox.java @@ -0,0 +1,60 @@ +package me.ByteEdit.boxes; + +import java.awt.EventQueue; +import java.awt.Toolkit; + +import javax.swing.JCheckBox; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.UIManager; +import javax.swing.border.EmptyBorder; + +public class OptionBox extends JFrame { + + private JPanel contentPane; + public JCheckBox chckbxComputeFrames; + public JCheckBox chckbxComputeMax; + + /** + * Launch the application. + */ + public static void main(String[] args) { + EventQueue.invokeLater(new Runnable() { + + public void run() { + try { + OptionBox frame = new OptionBox(); + frame.setVisible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + /** + * Create the frame. + */ + public OptionBox() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + e.printStackTrace(); + } + setResizable(false); + setTitle("Options"); + setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2 - 150), + (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2 - 100), 300, 200); + contentPane = new JPanel(); + contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); + setContentPane(contentPane); + contentPane.setLayout(null); + chckbxComputeFrames = new JCheckBox("Compute Frames"); + chckbxComputeFrames.setBounds(6, 7, 138, 23); + contentPane.add(chckbxComputeFrames); + chckbxComputeMax = new JCheckBox("Compute Max"); + chckbxComputeMax.setBounds(6, 33, 138, 23); + contentPane.add(chckbxComputeMax); + } +} diff --git a/src/me/ByteEdit/boxes/SearchBox.java b/src/me/ByteEdit/boxes/SearchBox.java new file mode 100644 index 0000000..19fbc39 --- /dev/null +++ b/src/me/ByteEdit/boxes/SearchBox.java @@ -0,0 +1,151 @@ +package me.ByteEdit.boxes; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.util.regex.Pattern; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.UIManager; +import javax.swing.border.EmptyBorder; + +import me.ByteEdit.main.Main; + +public class SearchBox extends JFrame { + + public JPanel contentPane; + public JTextField txtFind; + public JTextField txtReplace; + public JCheckBox chckbxCaseSensitive; + + /** + * Create the frame. + */ + public SearchBox() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + e.printStackTrace(); + } + setResizable(false); + setTitle("Find/Replace"); + setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2 - 128), + (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2 - 181), 256, 262); + contentPane = new JPanel(); + contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); + setContentPane(contentPane); + contentPane.setLayout(null); + JLabel lblFind = new JLabel("Find:"); + lblFind.setBounds(10, 25, 71, 14); + contentPane.add(lblFind); + txtFind = new JTextField(); + KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0); + txtFind.registerKeyboardAction(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + find(); + } + }, enter, JComponent.WHEN_IN_FOCUSED_WINDOW); + txtFind.setBounds(91, 22, 149, 20); + contentPane.add(txtFind); + txtFind.setColumns(10); + JLabel lblReplaceWith = new JLabel("Replace with:"); + lblReplaceWith.setBounds(10, 53, 71, 14); + contentPane.add(lblReplaceWith); + txtReplace = new JTextField(); + txtReplace.setColumns(10); + txtReplace.setBounds(91, 50, 149, 20); + contentPane.add(txtReplace); + chckbxCaseSensitive = new JCheckBox("Case sensitive"); + chckbxCaseSensitive.setBounds(10, 103, 230, 23); + contentPane.add(chckbxCaseSensitive); + JButton btnFind = new JButton("Find"); + btnFind.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + find(); + } + }); + btnFind.setBounds(10, 150, 110, 23); + contentPane.add(btnFind); + JButton btnReplacefind = new JButton("Replace/Find"); + btnReplacefind.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + replaceFind(); + } + }); + btnReplacefind.setBounds(130, 150, 110, 23); + contentPane.add(btnReplacefind); + JButton btnReplace = new JButton("Replace"); + btnReplace.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + replace(); + } + }); + btnReplace.setBounds(10, 184, 110, 23); + contentPane.add(btnReplace); + JButton btnReplaceAll = new JButton("Replace All"); + btnReplaceAll.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + replaceAll(); + } + }); + btnReplaceAll.setBounds(130, 184, 110, 23); + contentPane.add(btnReplaceAll); + } + + public void find() { + int startPos = Main.txtByteEditView.getCaretPosition(); + String toFind = txtFind.getText(); + String txt = chckbxCaseSensitive.isSelected() ? Main.txtByteEditView.getText().substring(startPos) + : Main.txtByteEditView.getText().substring(startPos).toLowerCase(); + int index = txt.indexOf(chckbxCaseSensitive.isSelected() ? toFind : toFind.toLowerCase()); + if (index == -1) { + txt = chckbxCaseSensitive.isSelected() ? Main.txtByteEditView.getText() : Main.txtByteEditView.getText().toLowerCase(); + index = txt.indexOf(chckbxCaseSensitive.isSelected() ? toFind : toFind.toLowerCase()); + startPos = 0; + if (index == -1) { + return; + } + } + Main.txtByteEditView.select(startPos + index, startPos + index + toFind.length()); + } + + public void replaceFind() { + replace(); + find(); + } + + public void replace() { + String txt = Main.txtByteEditView.getText(); + int startPos = Main.txtByteEditView.getSelectionStart(); + Main.txtByteEditView.replaceSelection(txtReplace.getText()); + Main.txtByteEditView.select(startPos, startPos + txtReplace.getText().length()); + } + + public void replaceAll() { + String toFind = txtFind.getText(); + String toReplace = txtReplace.getText(); + String txt = Main.txtByteEditView.getText(); + int prev = Main.txtByteEditView.getCaretPosition(); + if (chckbxCaseSensitive.isSelected()) { + Main.txtByteEditView.replaceRange(txt.replace(toFind, toReplace), 0, txt.length()); + } else { + Main.txtByteEditView.replaceRange(txt.replaceAll("(?i)" + Pattern.quote(toFind), toReplace), 0, txt.length()); + } + Main.txtByteEditView.setCaretPosition(prev); + } +} diff --git a/src/me/ByteEdit/boxes/TypeOpenBox.java b/src/me/ByteEdit/boxes/TypeOpenBox.java new file mode 100644 index 0000000..a58c06d --- /dev/null +++ b/src/me/ByteEdit/boxes/TypeOpenBox.java @@ -0,0 +1,119 @@ +package me.ByteEdit.boxes; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.DefaultListModel; +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.UIManager; +import javax.swing.border.EmptyBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import me.ByteEdit.main.Main; + +public class TypeOpenBox extends JFrame { + + public JPanel contentPane; + public JTextField txtSearch; + + /** + * Create the frame. + */ + public TypeOpenBox() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + e.printStackTrace(); + } + setResizable(false); + setTitle("Open Type"); + setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2 - 200), + (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2 - 140), 400, 280); + contentPane = new JPanel(); + contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); + setContentPane(contentPane); + contentPane.setLayout(null); + + DefaultListModel model = new DefaultListModel<>(); + JList list = new JList(model); + list.addListSelectionListener(new ListSelectionListener() { + + public void valueChanged(ListSelectionEvent e) { + String val = list.getSelectedValue(); + if (val != null && !val.equals(Main.currentNodeName)) { + Main.selectFile(val); + } + } + }); + + txtSearch = new JTextField(); + txtSearch.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + if (model.size() == 1) { + list.setSelectedIndex(0); + } + } + }); + txtSearch.getDocument().addDocumentListener(new DocumentListener() { + + public void changedUpdate(DocumentEvent e) { + updateList(); + } + + public void removeUpdate(DocumentEvent e) { + updateList(); + } + + public void insertUpdate(DocumentEvent e) { + updateList(); + } + + public void updateList() { + model.clear(); + if (txtSearch.getText().isEmpty()) { + return; + } + String search = txtSearch.getText().toLowerCase(); + if (search.contains("/")) { + for (String s : Main.classNodes.keySet()) { + String className = s.substring(0, s.length() - 6); + if (!className.contains("$") && className.toLowerCase().startsWith(search)) { + model.addElement(s); + } + } + } else { + for (String s : Main.classNodes.keySet()) { + String[] split = s.split("/"); + String className = split[split.length - 1]; + className = className.substring(0, className.length() - 6); + if (!className.contains("$") && className.toLowerCase().startsWith(search)) { + model.addElement(s); + } + } + } + } + }); + txtSearch.setBounds(10, 11, 374, 20); + contentPane.add(txtSearch); + txtSearch.setColumns(10); + + JScrollPane scrollPane = new JScrollPane(); + scrollPane.setBounds(10, 46, 374, 194); + contentPane.add(scrollPane); + + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + scrollPane.setViewportView(list); + } + +} diff --git a/src/me/ByteEdit/boxes/UnicodeBox.java b/src/me/ByteEdit/boxes/UnicodeBox.java new file mode 100644 index 0000000..d57c645 --- /dev/null +++ b/src/me/ByteEdit/boxes/UnicodeBox.java @@ -0,0 +1,77 @@ +package me.ByteEdit.boxes; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.UIManager; +import javax.swing.border.EmptyBorder; + +import me.ByteEdit.main.Main; +import me.ByteEdit.utils.UnicodeUtils; + +public class UnicodeBox extends JFrame { + + private JPanel contentPane; + + /** + * Create the frame. + */ + public UnicodeBox() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + e.printStackTrace(); + } + setResizable(false); + setTitle("Unicode Converter"); + setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2 - 150), + (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2 - 100), 300, 200); + contentPane = new JPanel(); + contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); + setContentPane(contentPane); + contentPane.setLayout(null); + + JTextArea textArea = new JTextArea(); + + JButton btnEscape = new JButton("Escape"); + btnEscape.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + try { + textArea.setText(UnicodeUtils.escape(textArea.getText())); + } catch (IllegalArgumentException ex) { + Main.showError(ex.getMessage()); + } + } + }); + btnEscape.setBounds(10, 137, 89, 23); + contentPane.add(btnEscape); + + JButton btnUnescape = new JButton("Unescape"); + btnUnescape.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + try { + textArea.setText(UnicodeUtils.unescape(textArea.getText())); + } catch (IllegalArgumentException ex) { + Main.showError(ex.getMessage()); + } + } + }); + btnUnescape.setBounds(195, 137, 89, 23); + contentPane.add(btnUnescape); + + JScrollPane scrollPane = new JScrollPane(); + scrollPane.setBounds(10, 11, 274, 115); + contentPane.add(scrollPane); + + scrollPane.setViewportView(textArea); + } +} diff --git a/src/me/ByteEdit/main/Main.java b/src/me/ByteEdit/main/Main.java index 596e3c0..65b395b 100644 --- a/src/me/ByteEdit/main/Main.java +++ b/src/me/ByteEdit/main/Main.java @@ -69,12 +69,13 @@ import org.objectweb.asm.ClassWriter; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; +import me.ByteEdit.boxes.OptionBox; +import me.ByteEdit.boxes.SearchBox; +import me.ByteEdit.boxes.TypeOpenBox; +import me.ByteEdit.boxes.UnicodeBox; import me.ByteEdit.edit.Assembler; import me.ByteEdit.edit.Disassembler; import me.ByteEdit.edit.Disassembler.DisassembleTuple; -import me.ByteEdit.edit.OptionBox; -import me.ByteEdit.edit.SearchBox; -import me.ByteEdit.edit.TypeOpenBox; import me.ByteEdit.utils.ClassUtil; import me.ByteEdit.utils.OpcodesReverse; import me.ByteEdit.utils.UnicodeUtils; @@ -92,6 +93,7 @@ public class Main extends JFrame { public static SearchBox searchBox; public static TypeOpenBox typeOpenBox; public static OptionBox optionBox; + public static UnicodeBox unicodeBox; public static JTree tree; public static RTextScrollPane scrollPane_ByteEdit; public static Theme theme; @@ -166,6 +168,7 @@ public class Main extends JFrame { searchBox = new SearchBox(); typeOpenBox = new TypeOpenBox(); optionBox = new OptionBox(); + unicodeBox = new UnicodeBox(); setTitle("ByteEdit"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2 - 407), @@ -322,6 +325,7 @@ public class Main extends JFrame { KeyStroke ctrlS = KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_DOWN_MASK); KeyStroke ctrlG = KeyStroke.getKeyStroke(KeyEvent.VK_G, KeyEvent.CTRL_DOWN_MASK); KeyStroke ctrlO = KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_DOWN_MASK); + KeyStroke ctrlU = KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK); // global txtByteEditView.registerKeyboardAction(new ActionListener() { @@ -348,6 +352,13 @@ public class Main extends JFrame { optionBox.setVisible(true); } }, ctrlO, JComponent.WHEN_IN_FOCUSED_WINDOW); + txtByteEditView.registerKeyboardAction(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + unicodeBox.setVisible(true); + } + }, ctrlU, JComponent.WHEN_IN_FOCUSED_WINDOW); // specific tree.registerKeyboardAction(new ActionListener() {