Code Cleanup

+ Refactored a lot of the GUI
+ Fixed a few bugs with the swing components and swing interaction
+ Includes #304
+ Added LAF theme selection
+ Moved the Visual Settings from the Settings menu to the View menu
+ Added a simplified tab names option
This commit is contained in:
Konloch 2021-06-25 18:13:46 -07:00
parent f1db4c2ea8
commit cec2df4685
88 changed files with 3207 additions and 3798 deletions

View file

@ -0,0 +1 @@
e1c0e9bdc5e1a9117385316e9953bf18

View file

@ -0,0 +1 @@
1f1bd37eb6a58bcbeca533757b452fda2e3bf58a

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bulenkov</groupId>
<artifactId>darcula</artifactId>
<version>2017.11bcv</version>
</project>

View file

@ -0,0 +1 @@
18d7207a865f9f1c7ac091ae2175c816

View file

@ -0,0 +1 @@
c24c87ad97daac49a716c8b1b35de813faa14315

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>com.bulenkov</groupId>
<artifactId>darcula</artifactId>
<versioning>
<release>2017.11bcv</release>
<versions>
<version>2017.11bcv</version>
</versions>
<lastUpdated>20210623081823</lastUpdated>
</versioning>
</metadata>

View file

@ -0,0 +1 @@
8c77ae68030a23cf5be1ad543a810476

View file

@ -0,0 +1 @@
890769d1a55221b9f778bfb92a8af22bfd3439df

29
pom.xml
View file

@ -219,30 +219,15 @@
<artifactId>D2Jar-obf</artifactId> <artifactId>D2Jar-obf</artifactId>
<version>1.0bcv</version> <version>1.0bcv</version>
</dependency> </dependency>
<dependency>
<groupId>com.bulenkov</groupId>
<artifactId>darcula</artifactId>
<version>2017.11bcv</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>unpack</id>
<!-- In process resource phase such that it doesn't
override other dependencies or BCV's own files -->
<phase>process-resources</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeScope>system</includeScope>
<outputDirectory>${basedir}/target/classes</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
@ -268,7 +253,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version> <version>3.3.0</version>
<configuration> <configuration>
<source>${java.version}</source> <source>${java.version}</source>
</configuration> </configuration>
@ -276,7 +261,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.2.2</version> <version>3.2.4</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <phase>package</phase>

View file

@ -1,3 +0,0 @@
Manifest-Version: 1.0
Main-Class: the.bytecode.club.bytecodeviewer.BytecodeViewer

View file

@ -10,11 +10,7 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import javax.swing.JDialog; import javax.swing.*;
import javax.swing.JFileChooser;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileFilter;
import me.konloch.kontainer.io.DiskWriter; import me.konloch.kontainer.io.DiskWriter;
import me.konloch.kontainer.io.HTTPRequest; import me.konloch.kontainer.io.HTTPRequest;
@ -23,13 +19,15 @@ import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bootloader.Boot; import the.bytecode.club.bootloader.Boot;
import the.bytecode.club.bytecodeviewer.api.ClassNodeLoader; import the.bytecode.club.bytecodeviewer.api.ClassNodeLoader;
import the.bytecode.club.bytecodeviewer.compilers.Compilers; import the.bytecode.club.bytecodeviewer.compilers.Compilers;
import the.bytecode.club.bytecodeviewer.gui.ClassViewer; import the.bytecode.club.bytecodeviewer.gui.components.DecompilerViewComponent;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.ResourcePanelCompileMode;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane; import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane;
import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI; import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI;
import the.bytecode.club.bytecodeviewer.gui.extras.RunOptions; import the.bytecode.club.bytecodeviewer.gui.components.RunOptions;
import the.bytecode.club.bytecodeviewer.gui.SearchBoxPane; import the.bytecode.club.bytecodeviewer.gui.resourcesearch.SearchBoxPane;
import the.bytecode.club.bytecodeviewer.gui.extras.SystemErrConsole; import the.bytecode.club.bytecodeviewer.gui.components.SystemErrConsole;
import the.bytecode.club.bytecodeviewer.gui.WorkPane; import the.bytecode.club.bytecodeviewer.gui.resourceviewer.WorkPaneMainComponent;
import the.bytecode.club.bytecodeviewer.obfuscators.mapping.Refactorer; import the.bytecode.club.bytecodeviewer.obfuscators.mapping.Refactorer;
import the.bytecode.club.bytecodeviewer.plugin.PluginManager; import the.bytecode.club.bytecodeviewer.plugin.PluginManager;
import the.bytecode.club.bytecodeviewer.util.*; import the.bytecode.club.bytecodeviewer.util.*;
@ -104,6 +102,7 @@ public class BytecodeViewer
public static Refactorer refactorer = new Refactorer(); public static Refactorer refactorer = new Refactorer();
public static List<FileContainer> files = new ArrayList<>(); //all of BCV's loaded files/classes/etc public static List<FileContainer> files = new ArrayList<>(); //all of BCV's loaded files/classes/etc
public static List<Process> createdProcesses = new ArrayList<>(); public static List<Process> createdProcesses = new ArrayList<>();
public static final DecompilerViewComponent krakatau = new DecompilerViewComponent("Krakatau", true);
public static final boolean EXPERIMENTAL_TAB_CODE = false; public static final boolean EXPERIMENTAL_TAB_CODE = false;
/** /**
@ -155,14 +154,16 @@ public class BytecodeViewer
* @throws IOException * @throws IOException
*/ */
public static byte[] getClassFile(Class<?> clazz) throws IOException { public static byte[] getClassFile(Class<?> clazz) throws IOException {
InputStream is = clazz.getResourceAsStream("/" + clazz.getName().replace('.', '/') + ".class"); try (InputStream is = clazz.getResourceAsStream(
ByteArrayOutputStream baos = new ByteArrayOutputStream(); "/" + clazz.getName().replace('.', '/') + ".class");
int r; ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[8192]; int r;
while ((r = Objects.requireNonNull(is).read(buffer)) >= 0) { byte[] buffer = new byte[8192];
baos.write(buffer, 0, r); while ((r = Objects.requireNonNull(is).read(buffer)) >= 0) {
baos.write(buffer, 0, r);
}
return baos.toByteArray();
} }
return baos.toByteArray();
} }
/** /**
@ -176,8 +177,11 @@ public class BytecodeViewer
System.setSecurityManager(sm); System.setSecurityManager(sm);
try { try {
UIManager.put("MenuItem.disabledAreNavigable", Boolean.FALSE); //precache settings file
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); Settings.preloadSettingsFile();
//setup look and feel
Configuration.lafTheme.setLAF();
if (PREVIEW_COPY && !CommandLineInput.containsCommand(args)) if (PREVIEW_COPY && !CommandLineInput.containsCommand(args))
showMessage("WARNING: This is a preview/dev copy, you WON'T be alerted when " + VERSION + " is " showMessage("WARNING: This is a preview/dev copy, you WON'T be alerted when " + VERSION + " is "
+ "actually out if you use this." + nl + + "actually out if you use this." + nl +
@ -378,28 +382,36 @@ public class BytecodeViewer
BytecodeViewer.viewer.updateBusyStatus(true); BytecodeViewer.viewer.updateBusyStatus(true);
boolean actuallyTried = false; boolean actuallyTried = false;
for (java.awt.Component c : BytecodeViewer.viewer.workPane.getLoadedViewers()) { for (java.awt.Component c : BytecodeViewer.viewer.workPane.getLoadedViewers())
if (c instanceof ClassViewer) { {
if (c instanceof ClassViewer)
{
ClassViewer cv = (ClassViewer) c; ClassViewer cv = (ClassViewer) c;
if (cv.smali1 != null && cv.smali1.isEditable() ||
cv.smali2 != null && cv.smali2.isEditable() || //compile smali assembly
cv.smali3 != null && cv.smali3.isEditable()) { if (cv.resourceViewPanel1.compileMode == ResourcePanelCompileMode.SMALI_ASSEMBLY && cv.resourceViewPanel1.textArea.isEditable() ||
cv.resourceViewPanel2.compileMode == ResourcePanelCompileMode.SMALI_ASSEMBLY && cv.resourceViewPanel2.textArea.isEditable() ||
cv.resourceViewPanel3.compileMode == ResourcePanelCompileMode.SMALI_ASSEMBLY && cv.resourceViewPanel3.textArea.isEditable())
{
actuallyTried = true; actuallyTried = true;
Object[] smali = cv.getSmali(); Object[] smali = cv.getSmali();
if (smali != null) { if (smali != null)
{
ClassNode origNode = (ClassNode) smali[0]; ClassNode origNode = (ClassNode) smali[0];
String smaliText = (String) smali[1]; String smaliText = (String) smali[1];
byte[] smaliCompiled = byte[] smaliCompiled = Compilers.smali.compile(smaliText, origNode.name);
Compilers.smali.compile(smaliText,
origNode.name); if (smaliCompiled != null)
if (smaliCompiled != null) { {
try { try {
ClassNode newNode = JarUtils.getNode(smaliCompiled); ClassNode newNode = JarUtils.getNode(smaliCompiled);
BytecodeViewer.updateNode(origNode, newNode); BytecodeViewer.updateNode(origNode, newNode);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} else { }
else
{
BytecodeViewer.showMessage("There has been an error with assembling your Smali code, " BytecodeViewer.showMessage("There has been an error with assembling your Smali code, "
+ "please check this. Class: " + origNode.name); + "please check this. Class: " + origNode.name);
BytecodeViewer.viewer.updateBusyStatus(false); BytecodeViewer.viewer.updateBusyStatus(false);
@ -408,26 +420,30 @@ public class BytecodeViewer
} }
} }
//compile krakatau assembly
if (cv.krakatau1 != null && cv.krakatau1.isEditable() || if (cv.resourceViewPanel1.compileMode == ResourcePanelCompileMode.KRAKATAU_ASSEMBLY && cv.resourceViewPanel1.textArea.isEditable() ||
cv.krakatau2 != null && cv.krakatau2.isEditable() || cv.resourceViewPanel2.compileMode == ResourcePanelCompileMode.KRAKATAU_ASSEMBLY && cv.resourceViewPanel2.textArea.isEditable() ||
cv.krakatau3 != null && cv.krakatau3.isEditable()) { cv.resourceViewPanel3.compileMode == ResourcePanelCompileMode.KRAKATAU_ASSEMBLY && cv.resourceViewPanel3.textArea.isEditable())
{
actuallyTried = true; actuallyTried = true;
Object[] krakatau = cv.getKrakatau(); Object[] krakatau = cv.getKrakatau();
if (krakatau != null) { if (krakatau != null)
{
ClassNode origNode = (ClassNode) krakatau[0]; ClassNode origNode = (ClassNode) krakatau[0];
String krakatauText = (String) krakatau[1]; String krakatauText = (String) krakatau[1];
byte[] krakatauCompiled = byte[] krakatauCompiled = Compilers.krakatau.compile(krakatauText, origNode.name);
Compilers.krakatau.compile(krakatauText,
origNode.name); if (krakatauCompiled != null)
if (krakatauCompiled != null) { {
try { try {
ClassNode newNode = JarUtils.getNode(krakatauCompiled); ClassNode newNode = JarUtils.getNode(krakatauCompiled);
BytecodeViewer.updateNode(origNode, newNode); BytecodeViewer.updateNode(origNode, newNode);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} else { }
else
{
BytecodeViewer.showMessage("There has been an error with assembling your Krakatau " BytecodeViewer.showMessage("There has been an error with assembling your Krakatau "
+ "Bytecode, please check this. Class: " + origNode.name); + "Bytecode, please check this. Class: " + origNode.name);
BytecodeViewer.viewer.updateBusyStatus(false); BytecodeViewer.viewer.updateBusyStatus(false);
@ -436,9 +452,11 @@ public class BytecodeViewer
} }
} }
if (cv.java1 != null && cv.java1.isEditable() || //default to java compiling
cv.java2 != null && cv.java2.isEditable() || if (cv.resourceViewPanel1.textArea != null && cv.resourceViewPanel1.textArea.isEditable() ||
cv.java3 != null && cv.java3.isEditable()) { cv.resourceViewPanel2.textArea != null && cv.resourceViewPanel2.textArea.isEditable() ||
cv.resourceViewPanel3.textArea != null && cv.resourceViewPanel3.textArea.isEditable())
{
actuallyTried = true; actuallyTried = true;
Object[] java = cv.getJava(); Object[] java = cv.getJava();
if (java != null) { if (java != null) {
@ -449,10 +467,9 @@ public class BytecodeViewer
errConsole.setText("Error compiling class: " + origNode.name + nl + "Keep in mind most " errConsole.setText("Error compiling class: " + origNode.name + nl + "Keep in mind most "
+ "decompilers cannot produce compilable classes" + nl + nl); + "decompilers cannot produce compilable classes" + nl + nl);
byte[] javaCompiled = byte[] javaCompiled = Compilers.java.compile(javaText, origNode.name);
Compilers.java.compile(javaText, if (javaCompiled != null)
origNode.name); {
if (javaCompiled != null) {
try { try {
ClassNode newNode = JarUtils.getNode(javaCompiled); ClassNode newNode = JarUtils.getNode(javaCompiled);
BytecodeViewer.updateNode(origNode, newNode); BytecodeViewer.updateNode(origNode, newNode);
@ -460,7 +477,9 @@ public class BytecodeViewer
e.printStackTrace(); e.printStackTrace();
} }
errConsole.finished(); errConsole.finished();
} else { }
else
{
errConsole.pretty(); errConsole.pretty();
errConsole.setVisible(true); errConsole.setVisible(true);
errConsole.finished(); errConsole.finished();
@ -554,7 +573,7 @@ public class BytecodeViewer
files.clear(); files.clear();
LazyNameUtil.reset(); LazyNameUtil.reset();
Objects.requireNonNull(MainViewerGUI.getComponent(ResourceListPane.class)).resetWorkspace(); Objects.requireNonNull(MainViewerGUI.getComponent(ResourceListPane.class)).resetWorkspace();
Objects.requireNonNull(MainViewerGUI.getComponent(WorkPane.class)).resetWorkspace(); Objects.requireNonNull(MainViewerGUI.getComponent(WorkPaneMainComponent.class)).resetWorkspace();
Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class)).resetWorkspace(); Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class)).resetWorkspace();
the.bytecode.club.bytecodeviewer.api.BytecodeViewer.getClassNodeLoader().clear(); the.bytecode.club.bytecodeviewer.api.BytecodeViewer.getClassNodeLoader().clear();
} }

View file

@ -8,7 +8,7 @@ import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Options; import org.apache.commons.cli.Options;
import org.objectweb.asm.ClassWriter; import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.decompilers.Decompilers; import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import the.bytecode.club.bytecodeviewer.util.JarUtils; import the.bytecode.club.bytecodeviewer.util.JarUtils;
import the.bytecode.club.bytecodeviewer.util.MiscUtils; import the.bytecode.club.bytecodeviewer.util.MiscUtils;
@ -200,12 +200,12 @@ public class CommandLineInput {
Thread.sleep(5 * 1000); Thread.sleep(5 * 1000);
if (target.equalsIgnoreCase("all")) { if (target.equalsIgnoreCase("all")) {
Decompilers.procyon.decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath()); Decompiler.PROCYON.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath());
} else { } else {
try { try {
ClassNode cn = BytecodeViewer.getClassNode(target); ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn); final ClassWriter cw = accept(cn);
String contents = Decompilers.procyon.decompileClassNode(cn, cw.toByteArray()); String contents = Decompiler.PROCYON.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false); DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) { } catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
@ -218,12 +218,12 @@ public class CommandLineInput {
Thread.sleep(5 * 1000); Thread.sleep(5 * 1000);
if (target.equalsIgnoreCase("all")) { if (target.equalsIgnoreCase("all")) {
Decompilers.cfr.decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath()); Decompiler.CFR.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath());
} else { } else {
try { try {
ClassNode cn = BytecodeViewer.getClassNode(target); ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn); final ClassWriter cw = accept(cn);
String contents = Decompilers.cfr.decompileClassNode(cn, cw.toByteArray()); String contents = Decompiler.CFR.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false); DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) { } catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
@ -236,12 +236,12 @@ public class CommandLineInput {
Thread.sleep(5 * 1000); Thread.sleep(5 * 1000);
if (target.equalsIgnoreCase("all")) { if (target.equalsIgnoreCase("all")) {
Decompilers.fernflower.decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath()); Decompiler.FERNFLOWER.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath());
} else { } else {
try { try {
ClassNode cn = BytecodeViewer.getClassNode(target); ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn); final ClassWriter cw = accept(cn);
String contents = Decompilers.fernflower.decompileClassNode(cn, cw.toByteArray()); String contents = Decompiler.FERNFLOWER.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false); DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) { } catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
@ -254,12 +254,12 @@ public class CommandLineInput {
Thread.sleep(5 * 1000); Thread.sleep(5 * 1000);
if (target.equalsIgnoreCase("all")) { if (target.equalsIgnoreCase("all")) {
Decompilers.krakatau.decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath()); Decompiler.KRAKATAU.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath());
} else { } else {
try { try {
ClassNode cn = BytecodeViewer.getClassNode(target); ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn); final ClassWriter cw = accept(cn);
String contents = Decompilers.krakatau.decompileClassNode(cn, cw.toByteArray()); String contents = Decompiler.KRAKATAU.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false); DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) { } catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
@ -278,7 +278,7 @@ public class CommandLineInput {
try { try {
ClassNode cn = BytecodeViewer.getClassNode(target); ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn); final ClassWriter cw = accept(cn);
String contents = Decompilers.krakatauDA.decompileClassNode(cn, cw.toByteArray()); String contents = Decompiler.KRAKATAU_BYTECODE.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false); DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) { } catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
@ -297,7 +297,7 @@ public class CommandLineInput {
try { try {
ClassNode cn = BytecodeViewer.getClassNode(target); ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn); final ClassWriter cw = accept(cn);
String contents = Decompilers.jdgui.decompileClassNode(cn, cw.toByteArray()); String contents = Decompiler.JDGUI.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false); DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) { } catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
@ -316,7 +316,26 @@ public class CommandLineInput {
try { try {
ClassNode cn = BytecodeViewer.getClassNode(target); ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn); final ClassWriter cw = accept(cn);
String contents = Decompilers.smali.decompileClassNode(cn, cw.toByteArray()); String contents = Decompiler.SMALI.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
} else if (decompiler.equalsIgnoreCase("jadx")) {
System.out.println("Decompiling " + input.getAbsolutePath() + " with JADX");
BytecodeViewer.openFiles(new File[]{input}, false);
Thread.sleep(5 * 1000);
if (target.equalsIgnoreCase("all")) {
System.out.println("Coming soon.");
//Decompiler.smali.decompileToZip(output.getAbsolutePath());
} else {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn);
String contents = Decompiler.JADX.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false); DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) { } catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);

View file

@ -1,8 +1,13 @@
package the.bytecode.club.bytecodeviewer; package the.bytecode.club.bytecodeviewer;
import the.bytecode.club.bytecodeviewer.gui.theme.LAFTheme;
import the.bytecode.club.bytecodeviewer.gui.theme.RSTATheme;
import java.io.File; import java.io.File;
/** /**
* A collection of variables that can be configured through the settings menu or some form of UI/plugin
*
* @author Konloch * @author Konloch
* @since 6/21/2021 * @since 6/21/2021
*/ */
@ -17,6 +22,7 @@ public class Configuration
public static File krakatauTempDir; public static File krakatauTempDir;
public static File krakatauTempJar; public static File krakatauTempJar;
public static boolean displayParentInTab = false; //also change in the main GUI public static boolean displayParentInTab = false; //also change in the main GUI
public static boolean simplifiedTabNames = false;
public static boolean currentlyDumping = false; public static boolean currentlyDumping = false;
public static boolean needsReDump = true; public static boolean needsReDump = true;
public static boolean warnForEditing = false; public static boolean warnForEditing = false;
@ -30,4 +36,7 @@ public class Configuration
public static boolean verifyCorruptedStateOnBoot = false; //eventually may be a setting public static boolean verifyCorruptedStateOnBoot = false; //eventually may be a setting
public static long lastHotKeyExecuted = System.currentTimeMillis(); public static long lastHotKeyExecuted = System.currentTimeMillis();
}
public static LAFTheme lafTheme = LAFTheme.LIGHT; //lightmode by default since it uses the system theme
public static RSTATheme rstaTheme = lafTheme.getRSTATheme();
}

View file

@ -10,6 +10,9 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* General program constants, to use this class include everything as a wildcard static import:
* import static the.bytecode.club.bytecodeviewer.Constants.*;
*
* @author Konloch * @author Konloch
* @since 6/21/2021 * @since 6/21/2021
*/ */

File diff suppressed because one or more lines are too long

View file

@ -3,6 +3,8 @@ package the.bytecode.club.bytecodeviewer;
import javax.swing.JFrame; import javax.swing.JFrame;
import me.konloch.kontainer.io.DiskReader; import me.konloch.kontainer.io.DiskReader;
import me.konloch.kontainer.io.DiskWriter; import me.konloch.kontainer.io.DiskWriter;
import the.bytecode.club.bytecodeviewer.gui.theme.LAFTheme;
import the.bytecode.club.bytecodeviewer.gui.theme.RSTATheme;
import static the.bytecode.club.bytecodeviewer.Constants.*; import static the.bytecode.club.bytecodeviewer.Constants.*;
@ -291,23 +293,48 @@ public class Settings {
String.valueOf(BytecodeViewer.viewer.ren.isSelected()), false); String.valueOf(BytecodeViewer.viewer.ren.isSelected()), false);
DiskWriter.writeNewLine(settingsName, DiskWriter.writeNewLine(settingsName,
String.valueOf(BytecodeViewer.viewer.viewPane1.getJADX().getEditable().isSelected()), false); String.valueOf(BytecodeViewer.viewer.viewPane1.getJADX().getEditable().isSelected()), false);
DiskWriter.writeNewLine(settingsName,
Configuration.lafTheme.name(), false);
DiskWriter.writeNewLine(settingsName,
Configuration.rstaTheme.name(), false);
DiskWriter.writeNewLine(settingsName,
String.valueOf(BytecodeViewer.viewer.simplifyNameInTabTitle.isSelected()), false);
} catch (Exception e) { } catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
} }
} }
public static void preloadSettingsFile()
{
try
{
//precache the file
DiskReader.loadString(settingsName, 0, true);
//process the cached file
Configuration.lafTheme = LAFTheme.valueOf(asString(127));
Configuration.rstaTheme = RSTATheme.valueOf(asString(128));
}
catch (Exception e)
{
//ignore because errors are expected, first start up and outdated settings.
//e.printStackTrace();
}
}
public static void loadSettings() { //utilizes the Disk Reader's caching system. public static void loadSettings() { //utilizes the Disk Reader's caching system.
try { try {
BytecodeViewer.viewer.rbr.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 1, true))); //parse the cached file from memory
BytecodeViewer.viewer.rsy.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 2, false))); BytecodeViewer.viewer.rbr.setSelected(asBoolean(1));
BytecodeViewer.viewer.din.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 3, false))); BytecodeViewer.viewer.rsy.setSelected(asBoolean(2));
BytecodeViewer.viewer.dc4.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 4, false))); BytecodeViewer.viewer.din.setSelected(asBoolean(3));
BytecodeViewer.viewer.das.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 5, false))); BytecodeViewer.viewer.dc4.setSelected(asBoolean(4));
BytecodeViewer.viewer.hes.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 6, false))); BytecodeViewer.viewer.das.setSelected(asBoolean(5));
BytecodeViewer.viewer.hdc.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 7, false))); BytecodeViewer.viewer.hes.setSelected(asBoolean(6));
BytecodeViewer.viewer.dgs.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 8, false))); BytecodeViewer.viewer.hdc.setSelected(asBoolean(7));
BytecodeViewer.viewer.ner.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 9, false))); BytecodeViewer.viewer.dgs.setSelected(asBoolean(8));
BytecodeViewer.viewer.den.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 10, false))); BytecodeViewer.viewer.ner.setSelected(asBoolean(9));
BytecodeViewer.viewer.den.setSelected(asBoolean(10));
BytecodeViewer.viewer.rgn.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 11, false))); BytecodeViewer.viewer.rgn.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 11, false)));
BytecodeViewer.viewer.bto.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 12, false))); BytecodeViewer.viewer.bto.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 12, false)));
BytecodeViewer.viewer.nns.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 13, false))); BytecodeViewer.viewer.nns.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 13, false)));
@ -378,30 +405,30 @@ public class Settings {
BytecodeViewer.viewer.debugHelpers.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 78, false))); BytecodeViewer.viewer.debugHelpers.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 78, false)));
//79 is deprecated //79 is deprecated
BytecodeViewer.viewer.updateCheck.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 80, false))); BytecodeViewer.viewer.updateCheck.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 80, false)));
BytecodeViewer.viewer.viewPane1.setSelectedViewer(getInt(81)); BytecodeViewer.viewer.viewPane1.setSelectedViewer(asInt(81));
BytecodeViewer.viewer.viewPane2.setSelectedViewer(getInt(82)); BytecodeViewer.viewer.viewPane2.setSelectedViewer(asInt(82));
BytecodeViewer.viewer.viewPane3.setSelectedViewer(getInt(83)); BytecodeViewer.viewer.viewPane3.setSelectedViewer(asInt(83));
BytecodeViewer.viewer.refreshOnChange.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 84, false))); BytecodeViewer.viewer.refreshOnChange.setSelected(asBoolean(84));
boolean bool = Boolean.parseBoolean(DiskReader.loadString(settingsName, 85, false)); boolean bool = Boolean.parseBoolean(asString(85));
if (bool) { if (bool) {
BytecodeViewer.viewer.setExtendedState(JFrame.MAXIMIZED_BOTH); BytecodeViewer.viewer.setExtendedState(JFrame.MAXIMIZED_BOTH);
BytecodeViewer.viewer.isMaximized = true; BytecodeViewer.viewer.isMaximized = true;
} }
//86 is deprecated //86 is deprecated
//87 is deprecated //87 is deprecated
Configuration.lastDirectory = DiskReader.loadString(settingsName, 88, false); Configuration.lastDirectory = asString(88);
Configuration.python = DiskReader.loadString(settingsName, 89, false); Configuration.python = asString(89);
Configuration.rt = DiskReader.loadString(settingsName, 90, false); Configuration.rt = asString(90);
BytecodeViewer.viewer.viewPane1.getProcyon().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 91, false))); BytecodeViewer.viewer.viewPane1.getProcyon().getEditable().setSelected(asBoolean(91));
BytecodeViewer.viewer.viewPane1.getCFR().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 92, false))); BytecodeViewer.viewer.viewPane1.getCFR().getEditable().setSelected(asBoolean(92));
BytecodeViewer.viewer.viewPane1.getFern().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 93, false))); BytecodeViewer.viewer.viewPane1.getFern().getEditable().setSelected(asBoolean(93));
BytecodeViewer.viewer.viewPane1.getKrakatau().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 94, false))); BytecodeViewer.viewer.viewPane1.getKrakatau().getEditable().setSelected(asBoolean(94));
BytecodeViewer.viewer.viewPane1.getSmali().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 95, false))); BytecodeViewer.viewer.viewPane1.getSmali().getEditable().setSelected(asBoolean(95));
BytecodeViewer.viewer.viewPane2.getProcyon().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 96, false))); BytecodeViewer.viewer.viewPane2.getProcyon().getEditable().setSelected(asBoolean(96));
BytecodeViewer.viewer.viewPane2.getCFR().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 97, false))); BytecodeViewer.viewer.viewPane2.getCFR().getEditable().setSelected(asBoolean(97));
BytecodeViewer.viewer.viewPane2.getFern().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 98, false))); BytecodeViewer.viewer.viewPane2.getFern().getEditable().setSelected(asBoolean(98));
BytecodeViewer.viewer.viewPane2.getKrakatau().getEditable().setSelected(asBoolean(99)); BytecodeViewer.viewer.viewPane2.getKrakatau().getEditable().setSelected(asBoolean(99));
BytecodeViewer.viewer.viewPane2.getSmali().getEditable().setSelected(asBoolean(100)); BytecodeViewer.viewer.viewPane2.getSmali().getEditable().setSelected(asBoolean(100));
BytecodeViewer.viewer.viewPane3.getProcyon().getEditable().setSelected(asBoolean(101)); BytecodeViewer.viewer.viewPane3.getProcyon().getEditable().setSelected(asBoolean(101));
@ -410,26 +437,31 @@ public class Settings {
BytecodeViewer.viewer.viewPane3.getKrakatau().getEditable().setSelected(asBoolean(104)); BytecodeViewer.viewer.viewPane3.getKrakatau().getEditable().setSelected(asBoolean(104));
BytecodeViewer.viewer.viewPane3.getSmali().getEditable().setSelected(asBoolean(105)); BytecodeViewer.viewer.viewPane3.getSmali().getEditable().setSelected(asBoolean(105));
BytecodeViewer.viewer.decodeAPKResources.setSelected(asBoolean(106)); BytecodeViewer.viewer.decodeAPKResources.setSelected(asBoolean(106));
Configuration.library = DiskReader.loadString(settingsName, 107, false); Configuration.library = asString(107);
Configuration.pingback = asBoolean(108); Configuration.pingback = asBoolean(108);
BytecodeViewer.viewer.viewPane1.getJD().getEditable().setSelected(asBoolean(109)); BytecodeViewer.viewer.viewPane1.getJD().getEditable().setSelected(asBoolean(109));
BytecodeViewer.viewer.viewPane2.getJD().getEditable().setSelected(asBoolean(110)); BytecodeViewer.viewer.viewPane2.getJD().getEditable().setSelected(asBoolean(110));
BytecodeViewer.viewer.viewPane3.getJD().getEditable().setSelected(asBoolean(111)); BytecodeViewer.viewer.viewPane3.getJD().getEditable().setSelected(asBoolean(111));
BytecodeViewer.viewer.fontSpinner.setValue(getInt(112)); BytecodeViewer.viewer.fontSpinner.setValue(asInt(112));
Configuration.deleteForeignLibraries = asBoolean(113); Configuration.deleteForeignLibraries = asBoolean(113);
int apkDecompiler = getInt(114);
if (apkDecompiler == 0) //APK Decompiler
BytecodeViewer.viewer.apkConversionGroup.setSelected(BytecodeViewer.viewer.apkConversionDex.getModel(), true); switch(asInt(114))
else if (apkDecompiler == 1) {
BytecodeViewer.viewer.apkConversionGroup.setSelected(BytecodeViewer.viewer.apkConversionEnjarify.getModel(), true); case 0:
BytecodeViewer.viewer.apkConversionGroup.setSelected(BytecodeViewer.viewer.apkConversionDex.getModel(), true);
break;
case 1:
BytecodeViewer.viewer.apkConversionGroup.setSelected(BytecodeViewer.viewer.apkConversionEnjarify.getModel(), true);
break;
}
Configuration.python3 = DiskReader.loadString(settingsName, 115, false); Configuration.python3 = asString(115);
Configuration.javac = DiskReader.loadString(settingsName, 116, false); Configuration.javac = asString(116);
Configuration.java = DiskReader.loadString(settingsName, 117, false); Configuration.java = asString(117);
BytecodeViewer.viewer.compileOnSave.setSelected(asBoolean(118)); BytecodeViewer.viewer.compileOnSave.setSelected(asBoolean(118));
BytecodeViewer.viewer.autoCompileOnRefresh.setSelected(asBoolean(119)); BytecodeViewer.viewer.autoCompileOnRefresh.setSelected(asBoolean(119));
Configuration.warnForEditing = Boolean.parseBoolean(DiskReader.loadString(settingsName, 120, false)); Configuration.warnForEditing = asBoolean(120);
BytecodeViewer.viewer.showFileInTabTitle.setSelected(asBoolean(121)); BytecodeViewer.viewer.showFileInTabTitle.setSelected(asBoolean(121));
Configuration.displayParentInTab = BytecodeViewer.viewer.showFileInTabTitle.isSelected(); Configuration.displayParentInTab = BytecodeViewer.viewer.showFileInTabTitle.isSelected();
BytecodeViewer.viewer.forcePureAsciiAsText.setSelected(asBoolean(122)); BytecodeViewer.viewer.forcePureAsciiAsText.setSelected(asBoolean(122));
@ -437,18 +469,27 @@ public class Settings {
BytecodeViewer.viewer.showClassMethods.setSelected(asBoolean(124)); BytecodeViewer.viewer.showClassMethods.setSelected(asBoolean(124));
BytecodeViewer.viewer.ren.setSelected(asBoolean(125)); BytecodeViewer.viewer.ren.setSelected(asBoolean(125));
BytecodeViewer.viewer.viewPane1.getJADX().getEditable().setSelected(asBoolean(126)); BytecodeViewer.viewer.viewPane1.getJADX().getEditable().setSelected(asBoolean(126));
//line 127 is used for theme on preload
//line 128 is used for theme on preload
BytecodeViewer.viewer.simplifyNameInTabTitle.setSelected(asBoolean(129));
Configuration.simplifiedTabNames = BytecodeViewer.viewer.simplifyNameInTabTitle.isSelected();
} catch (Exception e) { } catch (Exception e) {
//ignore because errors are expected, first start up and outdated settings. //ignore because errors are expected, first start up and outdated settings.
//e.printStackTrace(); //e.printStackTrace();
} }
} }
public static String asString(int lineNumber) throws Exception
{
return DiskReader.loadString(settingsName, lineNumber, false);
}
public static boolean asBoolean(int lineNumber) throws Exception public static boolean asBoolean(int lineNumber) throws Exception
{ {
return Boolean.parseBoolean(DiskReader.loadString(settingsName, lineNumber, false)); return Boolean.parseBoolean(DiskReader.loadString(settingsName, lineNumber, false));
} }
public static int getInt(int lineNumber) throws Exception public static int asInt(int lineNumber) throws Exception
{ {
return Integer.parseInt(DiskReader.loadString(settingsName, lineNumber, false)); return Integer.parseInt(DiskReader.loadString(settingsName, lineNumber, false));
} }

View file

@ -10,10 +10,11 @@ import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.compilers.Compilers; import the.bytecode.club.bytecodeviewer.compilers.Compilers;
import the.bytecode.club.bytecodeviewer.decompilers.InternalDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler; import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import the.bytecode.club.bytecodeviewer.decompilers.Decompilers;
import the.bytecode.club.bytecodeviewer.plugin.preinstalled.EZInjection; import the.bytecode.club.bytecodeviewer.plugin.preinstalled.EZInjection;
import the.bytecode.club.bytecodeviewer.util.JarUtils; import the.bytecode.club.bytecodeviewer.util.JarUtils;
import the.bytecode.club.bytecodeviewer.util.MiscUtils;
import static the.bytecode.club.bytecodeviewer.Constants.*; import static the.bytecode.club.bytecodeviewer.Constants.*;
@ -41,7 +42,6 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* *
* @author Konloch * @author Konloch
*/ */
public class BytecodeViewer { public class BytecodeViewer {
private static URLClassLoader cl; private static URLClassLoader cl;
@ -72,7 +72,7 @@ public class BytecodeViewer {
*/ */
public static List<Class<?>> loadClassesIntoClassLoader() { public static List<Class<?>> loadClassesIntoClassLoader() {
try { try {
File f = new File(tempDirectory + fs + "loaded_temp.jar"); File f = new File(tempDirectory + fs + MiscUtils.randomString(12) + "loaded_temp.jar");
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), f.getAbsolutePath()); JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), f.getAbsolutePath());
JarFile jarFile = new JarFile("" + f.getAbsolutePath()); JarFile jarFile = new JarFile("" + f.getAbsolutePath());
Enumeration<JarEntry> e = jarFile.entries(); Enumeration<JarEntry> e = jarFile.entries();
@ -201,8 +201,8 @@ public class BytecodeViewer {
* *
* @return The wrapped Krakatau Decompiler instance * @return The wrapped Krakatau Decompiler instance
*/ */
public static Decompiler getKrakatauDecompiler() { public static InternalDecompiler getKrakatauDecompiler() {
return Decompilers.krakatau; return Decompiler.KRAKATAU.getDecompiler();
} }
/** /**
@ -210,8 +210,8 @@ public class BytecodeViewer {
* *
* @return The wrapped Procyon Decompiler instance * @return The wrapped Procyon Decompiler instance
*/ */
public static Decompiler getProcyonDecompiler() { public static InternalDecompiler getProcyonDecompiler() {
return Decompilers.procyon; return Decompiler.PROCYON.getDecompiler();
} }
/** /**
@ -219,8 +219,8 @@ public class BytecodeViewer {
* *
* @return The wrapped CFR Decompiler instance * @return The wrapped CFR Decompiler instance
*/ */
public static Decompiler getCFRDecompiler() { public static InternalDecompiler getCFRDecompiler() {
return Decompilers.cfr; return Decompiler.CFR.getDecompiler();
} }
/** /**
@ -228,8 +228,8 @@ public class BytecodeViewer {
* *
* @return The wrapped FernFlower Decompiler instance * @return The wrapped FernFlower Decompiler instance
*/ */
public static Decompiler getFernFlowerDecompiler() { public static InternalDecompiler getFernFlowerDecompiler() {
return Decompilers.fernflower; return Decompiler.FERNFLOWER.getDecompiler();
} }
/** /**
@ -237,8 +237,26 @@ public class BytecodeViewer {
* *
* @return The wrapped Krakatau Disassembler instance * @return The wrapped Krakatau Disassembler instance
*/ */
public static Decompiler getKrakatauDisassembler() { public static InternalDecompiler getKrakatauDisassembler() {
return Decompilers.krakatauDA; return Decompiler.KRAKATAU_BYTECODE.getDecompiler();
}
/**
* Returns the wrapped JD-GUI Decompiler instance.
*
* @return The wrapped JD-GUI Decompiler instance
*/
public static InternalDecompiler getDJGUIDecompiler() {
return Decompiler.JDGUI.getDecompiler();
}
/**
* Returns the wrapped JADX Decompiler instance.
*
* @return The wrapped JADX Decompiler instance
*/
public static InternalDecompiler getJADXDecompiler() {
return Decompiler.JADX.getDecompiler();
} }
/** /**
@ -258,13 +276,4 @@ public class BytecodeViewer {
public static the.bytecode.club.bytecodeviewer.compilers.Compiler getSmaliCompiler() { public static the.bytecode.club.bytecodeviewer.compilers.Compiler getSmaliCompiler() {
return Compilers.smali; return Compilers.smali;
} }
/**
* Returns the wrapped JD-GUI Decompiler instance.
*
* @return The wrapped JD-GUI Decompiler instance
*/
public static the.bytecode.club.bytecodeviewer.decompilers.Decompiler getDJGUIDecompiler() {
return Decompilers.jdgui;
}
} }

View file

@ -1,7 +1,6 @@
package the.bytecode.club.bytecodeviewer.api; package the.bytecode.club.bytecodeviewer.api;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.awt.event.KeyListener; import java.awt.event.KeyListener;
@ -10,13 +9,14 @@ import javax.swing.JCheckBox;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField; import javax.swing.JTextField;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import the.bytecode.club.bytecodeviewer.BytecodeViewer; import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Resources; import the.bytecode.club.bytecodeviewer.Resources;
import the.bytecode.club.bytecodeviewer.gui.components.JFrameConsole;
import the.bytecode.club.bytecodeviewer.gui.components.SearchableJTextArea;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.PressKeyListener;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.ReleaseKeyListener;
/*************************************************************************** /***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite * * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
@ -42,219 +42,11 @@ import the.bytecode.club.bytecodeviewer.Resources;
* @author Konloch * @author Konloch
*/ */
public class PluginConsole extends JFrame { public class PluginConsole extends JFrameConsole
{
JTextArea textArea = new JTextArea(); public PluginConsole(String pluginName)
JPanel panel = new JPanel(new BorderLayout()); {
JScrollPane scrollPane = new JScrollPane(); super("Bytecode Viewer - Plugin Console - " + pluginName);
public JCheckBox check = new JCheckBox("Exact");
final JTextField field = new JTextField();
public PluginConsole(String pluginName) {
this.setIconImages(Resources.iconList);
setTitle("Bytecode Viewer - Plugin Console - " + pluginName);
setSize(new Dimension(542, 316));
getContentPane().add(scrollPane, BorderLayout.CENTER);
scrollPane.setViewportView(textArea);
textArea.addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {
if ((e.getKeyCode() == KeyEvent.VK_F) && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
field.requestFocus();
}
BytecodeViewer.checkHotKey(e);
}
@Override
public void keyReleased(KeyEvent arg0) {
}
@Override
public void keyTyped(KeyEvent arg0) {
}
});
JButton searchNext = new JButton();
JButton searchPrev = new JButton();
JPanel buttonPane = new JPanel(new BorderLayout());
buttonPane.add(searchNext, BorderLayout.WEST);
buttonPane.add(searchPrev, BorderLayout.EAST);
searchNext.setIcon(Resources.nextIcon);
searchPrev.setIcon(Resources.prevIcon);
panel.add(buttonPane, BorderLayout.WEST);
panel.add(field, BorderLayout.CENTER);
panel.add(check, BorderLayout.EAST);
searchNext.addActionListener(arg0 -> search(field.getText(), true));
searchPrev.addActionListener(arg0 -> search(field.getText(), false));
field.addKeyListener(new KeyListener() {
@Override
public void keyReleased(KeyEvent arg0) {
if (arg0.getKeyCode() == KeyEvent.VK_ENTER)
search(field.getText(), true);
}
@Override
public void keyPressed(KeyEvent arg0) {
}
@Override
public void keyTyped(KeyEvent arg0) {
}
});
scrollPane.setColumnHeaderView(panel);
this.setLocationRelativeTo(null);
}
/**
* This was really interesting to write.
*
* @author Konloch
*/
public void search(String search, boolean next) {
try {
JTextArea area = textArea;
if (search.isEmpty()) {
highlight(area, "");
return;
}
int startLine = area.getDocument().getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1;
int currentLine = 1;
boolean canSearch = false;
String[] test;
if (area.getText().split("\n").length >= 2)
test = area.getText().split("\n");
else
test = area.getText().split("\r");
int lastGoodLine = -1;
int firstPos = -1;
boolean found = false;
if (next) {
for (String s : test) {
if (!check.isSelected()) {
s = s.toLowerCase();
search = search.toLowerCase();
}
if (currentLine == startLine) {
canSearch = true;
} else if (s.contains(search)) {
if (canSearch) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(currentLine - 1)
.getStartOffset());
canSearch = false;
found = true;
}
if (firstPos == -1)
firstPos = currentLine;
}
currentLine++;
}
if (!found && firstPos != -1) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement().getElement(firstPos - 1)
.getStartOffset());
}
} else {
canSearch = true;
for (String s : test) {
if (!check.isSelected()) {
s = s.toLowerCase();
search = search.toLowerCase();
}
if (s.contains(search)) {
if (lastGoodLine != -1 && canSearch)
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1)
.getStartOffset());
lastGoodLine = currentLine;
if (currentLine >= startLine)
canSearch = false;
}
currentLine++;
}
if (lastGoodLine != -1
&& area.getDocument().getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1 == startLine) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1).getStartOffset());
}
}
highlight(area, search);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
private final DefaultHighlighter.DefaultHighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(
new Color(255, 62, 150));
public void highlight(JTextComponent textComp, String pattern) {
if (pattern.isEmpty()) {
textComp.getHighlighter().removeAllHighlights();
return;
}
try {
Highlighter hilite = textComp.getHighlighter();
hilite.removeAllHighlights();
javax.swing.text.Document doc = textComp.getDocument();
String text = doc.getText(0, doc.getLength());
int pos = 0;
if (!check.isSelected()) {
pattern = pattern.toLowerCase();
text = text.toLowerCase();
}
// Search for pattern
while ((pos = text.indexOf(pattern, pos)) >= 0) {
// Create highlighter using private painter and apply around
// pattern
hilite.addHighlight(pos, pos + pattern.length(), painter);
pos += pattern.length();
}
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
/**
* Appends \r\n to the end of your string, then it puts it on the top.
*
* @param t the string you want to append
*/
public void appendText(String t) {
textArea.setText((textArea.getText().isEmpty() ? "" : textArea
.getText() + "\r\n")
+ t);
textArea.setCaretPosition(0);
}
/**
* Sets the text
*
* @param t the text you want set
*/
public void setText(String t) {
textArea.setText(t);
textArea.setCaretPosition(0);
} }
private static final long serialVersionUID = -6556940545421437508L; private static final long serialVersionUID = -6556940545421437508L;

View file

@ -29,7 +29,8 @@ import org.objectweb.asm.util.TraceClassVisitor;
* *
* @author Thiakil * @author Thiakil
*/ */
public class ASMTextifierDecompiler extends Decompiler { public class ASMTextifierDecompiler extends InternalDecompiler
{
@Override @Override
public String decompileClassNode(ClassNode cn, byte[] b) { public String decompileClassNode(ClassNode cn, byte[] b) {
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();

View file

@ -46,7 +46,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author Konloch * @author Konloch
*/ */
public class CFRDecompiler extends Decompiler { public class CFRDecompiler extends InternalDecompiler
{
private static final String[] WINDOWS_IS_GREAT = new String[] private static final String[] WINDOWS_IS_GREAT = new String[]
{ {

View file

@ -1,6 +1,11 @@
package the.bytecode.club.bytecodeviewer.decompilers; package the.bytecode.club.bytecodeviewer.decompilers;
import org.objectweb.asm.tree.ClassNode; import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.decompilers.bytecode.ClassNodeDecompiler;
import the.bytecode.club.bytecodeviewer.gui.components.DecompilerViewComponent;
import javax.swing.*;
import java.util.HashMap;
/*************************************************************************** /***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite * * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
@ -21,15 +26,90 @@ import org.objectweb.asm.tree.ClassNode;
***************************************************************************/ ***************************************************************************/
/** /**
* Used to represent all of the decompilers/disassemblers BCV contains. * All of the decompilers/disassemblers BCV uses
* *
* @author Konloch * @author Konloch
*/ */
public enum Decompiler
public abstract class Decompiler { {
NONE(0, "None", null, (JRadioButtonMenuItem) null),
public abstract String decompileClassNode(ClassNode cn, byte[] b); PROCYON(1, "Procyon Decompiler", new ProcyonDecompiler(), new DecompilerViewComponent("Procyon")),
CFR(2, "CFR Decompiler", new CFRDecompiler(), new DecompilerViewComponent("Procyon")),
public abstract void decompileToZip(String sourceJar, String zipName); FERNFLOWER(3, "FernFlower Decompiler", new FernFlowerDecompiler(), new DecompilerViewComponent("Procyon")),
BYTECODE(4, "Bytecode Disassembler", new ClassNodeDecompiler(), new JRadioButtonMenuItem("Bytecode")),
} HEXCODE(5, "Hexcode Viewer", null, new JRadioButtonMenuItem("Hexcode")),
SMALI(6, "Smali Decompiler", new SmaliDisassembler(), new DecompilerViewComponent("Smali")),
KRAKATAU(7, "Krakatau Decompiler", new KrakatauDecompiler(), BytecodeViewer.krakatau),
KRAKATAU_BYTECODE(8, "Krakatau Disassembler", new KrakatauDisassembler(), BytecodeViewer.krakatau),
JDGUI(9, "JD-GUI Decompiler", new JDGUIDecompiler(), new DecompilerViewComponent("Bytecode")),
JADX(10, "JADX Decompiler", new JADXDecompiler(), new DecompilerViewComponent("JADX")),
ASMTextify(11, "ASM Disassembler", new ASMTextifierDecompiler(), new DecompilerViewComponent("ASM Textify")),
;
private final int decompilerIndex;
private final String decompilerName;
private final InternalDecompiler decompiler;
private final DecompilerViewComponent decompilerSelectComponent;
private final JRadioButtonMenuItem basicSelectComponent;
public static final HashMap<Integer, Decompiler> decompilersByIndex = new HashMap<>();
static
{
for(Decompiler d : values())
decompilersByIndex.put(d.decompilerIndex, d);
}
Decompiler(int decompilerIndex, String decompilerName, InternalDecompiler decompiler, DecompilerViewComponent decompilerSelectComponent) {
this.decompilerIndex = decompilerIndex;
this.decompilerName = decompilerName;
this.decompiler = decompiler;
this.decompilerSelectComponent = decompilerSelectComponent;
this.basicSelectComponent = null;
}
Decompiler(int decompilerIndex, String decompilerName, InternalDecompiler decompiler, JRadioButtonMenuItem basicSelectComponent)
{
this.decompilerIndex = decompilerIndex;
this.decompilerName = decompilerName;
this.decompiler = decompiler;
this.decompilerSelectComponent = null;
this.basicSelectComponent = basicSelectComponent;
}
public void addDecompilerToGroup(ButtonGroup group)
{
if(decompilerSelectComponent != null)
decompilerSelectComponent.addToGroup(group);
else if(basicSelectComponent != null)
group.add(basicSelectComponent);
}
public int getDecompilerIndex()
{
return decompilerIndex;
}
public String getDecompilerName()
{
return decompilerName;
}
public InternalDecompiler getDecompiler()
{
return decompiler;
}
public DecompilerViewComponent getDecompilerSelectComponent()
{
return decompilerSelectComponent;
}
public JMenuItem getMenu()
{
if(decompilerSelectComponent != null)
return decompilerSelectComponent.getMenu();
return basicSelectComponent;
}
}

View file

@ -1,18 +0,0 @@
package the.bytecode.club.bytecodeviewer.decompilers;
import the.bytecode.club.bytecodeviewer.decompilers.bytecode.ClassNodeDecompiler;
public class Decompilers {
public final static Decompiler bytecode = new ClassNodeDecompiler();
public final static Decompiler fernflower = new FernFlowerDecompiler();
public final static Decompiler procyon = new ProcyonDecompiler();
public final static Decompiler cfr = new CFRDecompiler();
public final static KrakatauDecompiler krakatau = new KrakatauDecompiler();
public final static KrakatauDisassembler krakatauDA = new KrakatauDisassembler();
public final static SmaliDisassembler smali = new SmaliDisassembler();
public final static Decompiler jdgui = new JDGUIDecompiler();
public final static Decompiler jadx = new JADXDecompiler();
public final static Decompiler textifier = new ASMTextifierDecompiler();
}

View file

@ -38,7 +38,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author Konloch * @author Konloch
* @author WaterWolf * @author WaterWolf
*/ */
public class FernFlowerDecompiler extends Decompiler { public class FernFlowerDecompiler extends InternalDecompiler
{
@Override @Override
public void decompileToZip(String sourceJar, String zipName) { public void decompileToZip(String sourceJar, String zipName) {

View file

@ -0,0 +1,33 @@
package the.bytecode.club.bytecodeviewer.decompilers;
import org.objectweb.asm.tree.ClassNode;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* Used to represent a decompiler/disassembler
*
* @author Konloch
*/
public abstract class InternalDecompiler
{
public abstract String decompileClassNode(ClassNode cn, byte[] b);
public abstract void decompileToZip(String sourceJar, String zipName);
}

View file

@ -38,7 +38,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* *
* @author Konloch * @author Konloch
*/ */
public class JADXDecompiler extends Decompiler { public class JADXDecompiler extends InternalDecompiler
{
@Override @Override
public String decompileClassNode(ClassNode cn, byte[] b) { public String decompileClassNode(ClassNode cn, byte[] b) {
String fileStart = tempDirectory + fs; String fileStart = tempDirectory + fs;

View file

@ -13,7 +13,6 @@ import jd.cli.util.ClassFileUtil;
import me.konloch.kontainer.io.DiskReader; import me.konloch.kontainer.io.DiskReader;
import org.jd.core.v1.ClassFileToJavaSourceDecompiler; import org.jd.core.v1.ClassFileToJavaSourceDecompiler;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Constants; import the.bytecode.club.bytecodeviewer.Constants;
import the.bytecode.club.bytecodeviewer.util.MiscUtils; import the.bytecode.club.bytecodeviewer.util.MiscUtils;
@ -44,7 +43,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author JD-Core developers * @author JD-Core developers
*/ */
public class JDGUIDecompiler extends Decompiler { public class JDGUIDecompiler extends InternalDecompiler
{
@Override @Override
public String decompileClassNode(ClassNode cn, byte[] b) { public String decompileClassNode(ClassNode cn, byte[] b) {

View file

@ -42,7 +42,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author Konloch * @author Konloch
*/ */
public class KrakatauDecompiler extends Decompiler { public class KrakatauDecompiler extends InternalDecompiler
{
public String quick() { public String quick() {
if (Configuration.library.isEmpty()) if (Configuration.library.isEmpty())

View file

@ -41,7 +41,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author Konloch * @author Konloch
*/ */
public class KrakatauDisassembler extends Decompiler { public class KrakatauDisassembler extends InternalDecompiler
{
public String decompileClassNode(File krakatauTempJar, File krakatauTempDir, ClassNode cn) { public String decompileClassNode(File krakatauTempJar, File krakatauTempDir, ClassNode cn) {
if (Configuration.python.isEmpty()) { if (Configuration.python.isEmpty()) {

View file

@ -61,9 +61,10 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author Konloch * @author Konloch
* @author DeathMarine * @author DeathMarine
*/ */
public class ProcyonDecompiler extends Decompiler { public class ProcyonDecompiler extends InternalDecompiler
{
public DecompilerSettings getDecompilerSettings() { public DecompilerSettings getDecompilerSettings()
{
DecompilerSettings settings = new DecompilerSettings(); DecompilerSettings settings = new DecompilerSettings();
settings.setAlwaysGenerateExceptionVariableForCatchBlocks(BytecodeViewer.viewer.alwaysGenerateExceptionVars.isSelected()); settings.setAlwaysGenerateExceptionVariableForCatchBlocks(BytecodeViewer.viewer.alwaysGenerateExceptionVars.isSelected());
settings.setExcludeNestedTypes(BytecodeViewer.viewer.excludeNestedTypes.isSelected()); settings.setExcludeNestedTypes(BytecodeViewer.viewer.excludeNestedTypes.isSelected());

View file

@ -9,7 +9,6 @@ import java.util.Objects;
import me.konloch.kontainer.io.DiskReader; import me.konloch.kontainer.io.DiskReader;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.util.Dex2Jar; import the.bytecode.club.bytecodeviewer.util.Dex2Jar;
import the.bytecode.club.bytecodeviewer.util.FileContainer; import the.bytecode.club.bytecodeviewer.util.FileContainer;
import the.bytecode.club.bytecodeviewer.util.MiscUtils; import the.bytecode.club.bytecodeviewer.util.MiscUtils;
@ -40,7 +39,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author Konloch * @author Konloch
*/ */
public class SmaliDisassembler extends Decompiler { public class SmaliDisassembler extends InternalDecompiler
{
public String decompileClassNode(FileContainer container, ClassNode cn, byte[] b) { public String decompileClassNode(FileContainer container, ClassNode cn, byte[] b) {
String exception = ""; String exception = "";

View file

@ -9,7 +9,7 @@ import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InnerClassNode; import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.MethodNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer; import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler; import the.bytecode.club.bytecodeviewer.decompilers.InternalDecompiler;
import static the.bytecode.club.bytecodeviewer.Constants.*; import static the.bytecode.club.bytecodeviewer.Constants.*;
@ -36,7 +36,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author Bibl * @author Bibl
*/ */
public class ClassNodeDecompiler extends Decompiler { public class ClassNodeDecompiler extends InternalDecompiler
{
@Override @Override
public String decompileClassNode(ClassNode cn, byte[] b) { public String decompileClassNode(ClassNode cn, byte[] b) {

View file

@ -1,328 +0,0 @@
package the.bytecode.club.bytecodeviewer.gui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.imgscalr.Scalr;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Resources;
import the.bytecode.club.bytecodeviewer.gui.hexviewer.JHexEditor;
import the.bytecode.club.bytecodeviewer.util.FileContainer;
import the.bytecode.club.bytecodeviewer.util.Language;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* Represents any open non-class file.
*
* @author Konloch
*/
public class FileViewer extends Viewer {
private static final long serialVersionUID = 6103372882168257164L;
String name;
private final byte[] contents;
RSyntaxTextArea panelArea = new RSyntaxTextArea();
JPanel panel = new JPanel(new BorderLayout());
JPanel panel2 = new JPanel(new BorderLayout());
public JCheckBox check = new JCheckBox("Exact");
final JTextField field = new JTextField();
public BufferedImage image;
boolean canRefresh = false;
public TabbedPane tabbedPane;
public void setContents() {
String name = this.name.toLowerCase();
panelArea.setCodeFoldingEnabled(true);
panelArea.setAntiAliasingEnabled(true);
RTextScrollPane scrollPane = new RTextScrollPane(panelArea);
panelArea.addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {
if ((e.getKeyCode() == KeyEvent.VK_F) && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
field.requestFocus();
}
BytecodeViewer.checkHotKey(e);
}
@Override
public void keyReleased(KeyEvent arg0) {
}
@Override
public void keyTyped(KeyEvent arg0) {
}
});
String contentsS = new String(contents);
if (!isPureAscii(contentsS)) {
if (name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg") ||
name.endsWith(".gif") || name.endsWith(".tif") || name.endsWith(".bmp")) {
canRefresh = true;
try {
image = ImageIO.read(new ByteArrayInputStream(contents)); //gifs fail cause of this
JLabel label = new JLabel("", new ImageIcon(image), JLabel.CENTER);
panel2.add(label, BorderLayout.CENTER);
panel2.addMouseWheelListener(e -> {
int notches = e.getWheelRotation();
if (notches < 0) {
image = Scalr.resize(image, Scalr.Method.SPEED, image.getWidth() + 10,
image.getHeight() + 10);
} else {
image = Scalr.resize(image, Scalr.Method.SPEED, image.getWidth() - 10,
image.getHeight() - 10);
}
panel2.removeAll();
JLabel label1 = new JLabel("", new ImageIcon(image), JLabel.CENTER);
panel2.add(label1, BorderLayout.CENTER);
panel2.updateUI();
});
return;
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
} else if (BytecodeViewer.viewer.forcePureAsciiAsText.isSelected()) {
JHexEditor hex = new JHexEditor(contents);
panel2.add(hex);
return;
}
}
panelArea.setSyntaxEditingStyle(Language.detectLanguage(name, contentsS).getSyntaxConstant());
panelArea.setText(contentsS);
panelArea.setCaretPosition(0);
scrollPane.setColumnHeaderView(panel);
panel2.add(scrollPane);
}
static CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder(); // or "ISO-8859-1" for ISO Latin 1
public static boolean isPureAscii(String v) {
return asciiEncoder.canEncode(v);
}
public FileViewer(final FileContainer container, final String name, final byte[] contents) {
this.name = name;
this.contents = contents;
this.container = container;
this.setName(name);
this.setLayout(new BorderLayout());
this.add(panel2, BorderLayout.CENTER);
JButton searchNext = new JButton();
JButton searchPrev = new JButton();
JPanel buttonPane = new JPanel(new BorderLayout());
buttonPane.add(searchNext, BorderLayout.WEST);
buttonPane.add(searchPrev, BorderLayout.EAST);
searchNext.setIcon(Resources.nextIcon);
searchPrev.setIcon(Resources.prevIcon);
panel.add(buttonPane, BorderLayout.WEST);
panel.add(field, BorderLayout.CENTER);
panel.add(check, BorderLayout.EAST);
searchNext.addActionListener(arg0 -> search(field.getText(), true));
searchPrev.addActionListener(arg0 -> search(field.getText(), false));
field.addKeyListener(new KeyListener() {
@Override
public void keyReleased(KeyEvent arg0) {
if (arg0.getKeyCode() == KeyEvent.VK_ENTER)
search(field.getText(), true);
}
@Override
public void keyPressed(KeyEvent arg0) {
}
@Override
public void keyTyped(KeyEvent arg0) {
}
});
setContents();
}
/**
* This was really interesting to write.
*
* @author Konloch
*/
public void search(String search, boolean next) {
try {
JTextArea area = panelArea;
if (search.isEmpty()) {
highlight(area, "");
return;
}
int startLine = area.getDocument().getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1;
int currentLine = 1;
boolean canSearch = false;
String[] test;
if (area.getText().split("\n").length >= 2)
test = area.getText().split("\n");
else
test = area.getText().split("\r");
int lastGoodLine = -1;
int firstPos = -1;
boolean found = false;
if (next) {
for (String s : test) {
if (!check.isSelected()) {
s = s.toLowerCase();
search = search.toLowerCase();
}
if (currentLine == startLine) {
canSearch = true;
} else if (s.contains(search)) {
if (canSearch) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(currentLine - 1)
.getStartOffset());
canSearch = false;
found = true;
}
if (firstPos == -1)
firstPos = currentLine;
}
currentLine++;
}
if (!found && firstPos != -1) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement().getElement(firstPos - 1)
.getStartOffset());
}
} else {
canSearch = true;
for (String s : test) {
if (!check.isSelected()) {
s = s.toLowerCase();
search = search.toLowerCase();
}
if (s.contains(search)) {
if (lastGoodLine != -1 && canSearch)
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1)
.getStartOffset());
lastGoodLine = currentLine;
if (currentLine >= startLine)
canSearch = false;
}
currentLine++;
}
if (lastGoodLine != -1
&& area.getDocument().getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1 == startLine) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1).getStartOffset());
}
}
highlight(area, search);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
private final DefaultHighlighter.DefaultHighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(
new Color(255, 62, 150));
public void highlight(JTextComponent textComp, String pattern) {
if (pattern.isEmpty()) {
textComp.getHighlighter().removeAllHighlights();
return;
}
try {
Highlighter hilite = textComp.getHighlighter();
hilite.removeAllHighlights();
javax.swing.text.Document doc = textComp.getDocument();
String text = doc.getText(0, doc.getLength());
int pos = 0;
if (!check.isSelected()) {
pattern = pattern.toLowerCase();
text = text.toLowerCase();
}
// Search for pattern
while ((pos = text.indexOf(pattern, pos)) >= 0) {
// Create highlighter using private painter and apply around
// pattern
hilite.addHighlight(pos, pos + pattern.length(), painter);
pos += pattern.length();
}
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
public void refresh(JButton src) {
if (!canRefresh) {
src.setEnabled(true);
return;
}
panel2.removeAll();
try {
image = ImageIO.read(new ByteArrayInputStream(contents));
} catch (IOException e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
JLabel label = new JLabel("", new ImageIcon(image), JLabel.CENTER);
panel2.add(label, BorderLayout.CENTER);
panel2.updateUI();
src.setEnabled(true);
}
}

View file

@ -4,22 +4,9 @@ import java.awt.Dimension;
import java.awt.KeyboardFocusManager; import java.awt.KeyboardFocusManager;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import javax.swing.BoxLayout; import java.util.HashMap;
import javax.swing.ButtonGroup; import java.util.Map;
import javax.swing.JCheckBoxMenuItem; import javax.swing.*;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JSeparator;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileFilter;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
@ -28,11 +15,17 @@ import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.Resources; import the.bytecode.club.bytecodeviewer.Resources;
import the.bytecode.club.bytecodeviewer.Settings; import the.bytecode.club.bytecodeviewer.Settings;
import the.bytecode.club.bytecodeviewer.api.ExceptionUI; import the.bytecode.club.bytecodeviewer.api.ExceptionUI;
import the.bytecode.club.bytecodeviewer.gui.extras.AboutWindow; import the.bytecode.club.bytecodeviewer.gui.components.VisibleComponent;
import the.bytecode.club.bytecodeviewer.gui.extras.RunOptions; import the.bytecode.club.bytecodeviewer.gui.components.AboutWindow;
import the.bytecode.club.bytecodeviewer.gui.components.RunOptions;
import the.bytecode.club.bytecodeviewer.gui.plugins.MaliciousCodeScannerOptions; import the.bytecode.club.bytecodeviewer.gui.plugins.MaliciousCodeScannerOptions;
import the.bytecode.club.bytecodeviewer.gui.plugins.ReplaceStringsOptions; import the.bytecode.club.bytecodeviewer.gui.plugins.ReplaceStringsOptions;
import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane; import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane;
import the.bytecode.club.bytecodeviewer.gui.resourcesearch.SearchBoxPane;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.DecompilerSelectionPane;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.WorkPaneMainComponent;
import the.bytecode.club.bytecodeviewer.gui.theme.LAFTheme;
import the.bytecode.club.bytecodeviewer.gui.theme.RSTATheme;
import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameClasses; import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameClasses;
import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameFields; import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameFields;
import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameMethods; import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameMethods;
@ -80,7 +73,7 @@ public class MainViewerGUI extends JFrame {
//main UI components //main UI components
private static final ArrayList<VisibleComponent> uiComponents = new ArrayList<>(); private static final ArrayList<VisibleComponent> uiComponents = new ArrayList<>();
public final WorkPane workPane = new WorkPane(); public final WorkPaneMainComponent workPane = new WorkPaneMainComponent();
public final ResourceListPane resourcePane = new ResourceListPane(); public final ResourceListPane resourcePane = new ResourceListPane();
public final SearchBoxPane searchBoxPane = new SearchBoxPane(); public final SearchBoxPane searchBoxPane = new SearchBoxPane();
public JSplitPane splitPane1; public JSplitPane splitPane1;
@ -108,9 +101,9 @@ public class MainViewerGUI extends JFrame {
//all of the view main menu components //all of the view main menu components
public final JMenu viewMainMenu = new JMenu("View"); public final JMenu viewMainMenu = new JMenu("View");
public final ViewPane viewPane1 = new ViewPane(1); public final DecompilerSelectionPane viewPane1 = new DecompilerSelectionPane(1);
public final ViewPane viewPane2 = new ViewPane(2); public final DecompilerSelectionPane viewPane2 = new DecompilerSelectionPane(2);
public final ViewPane viewPane3 = new ViewPane(3); public final DecompilerSelectionPane viewPane3 = new DecompilerSelectionPane(3);
//all of the plugins main menu components //all of the plugins main menu components
public final JMenu pluginsMainMenu = new JMenu("Plugins"); public final JMenu pluginsMainMenu = new JMenu("Plugins");
@ -132,6 +125,8 @@ public class MainViewerGUI extends JFrame {
public final JRadioButtonMenuItem apkConversionEnjarify = new JRadioButtonMenuItem("Enjarify"); public final JRadioButtonMenuItem apkConversionEnjarify = new JRadioButtonMenuItem("Enjarify");
public final JMenu fontSize = new JMenu("Font Size"); public final JMenu fontSize = new JMenu("Font Size");
public final JSpinner fontSpinner = new JSpinner(); public final JSpinner fontSpinner = new JSpinner();
public final JMenu rstaTheme = new JMenu("Text Area Theme");
public final JMenu lafTheme = new JMenu("Window Theme");
//BCV settings //BCV settings
public final JCheckBoxMenuItem refreshOnChange = new JCheckBoxMenuItem("Refresh On View Change"); public final JCheckBoxMenuItem refreshOnChange = new JCheckBoxMenuItem("Refresh On View Change");
private final JCheckBoxMenuItem deleteForeignOutdatedLibs = new JCheckBoxMenuItem("Delete Foreign/Outdated Libs"); private final JCheckBoxMenuItem deleteForeignOutdatedLibs = new JCheckBoxMenuItem("Delete Foreign/Outdated Libs");
@ -147,11 +142,14 @@ public class MainViewerGUI extends JFrame {
public final JMenuItem setOptionalLibrary = new JMenuItem("Set Optional Library Folder"); public final JMenuItem setOptionalLibrary = new JMenuItem("Set Optional Library Folder");
public final JCheckBoxMenuItem compileOnSave = new JCheckBoxMenuItem("Compile On Save"); public final JCheckBoxMenuItem compileOnSave = new JCheckBoxMenuItem("Compile On Save");
public final JCheckBoxMenuItem showFileInTabTitle = new JCheckBoxMenuItem("Show File In Tab Title"); public final JCheckBoxMenuItem showFileInTabTitle = new JCheckBoxMenuItem("Show File In Tab Title");
public final JCheckBoxMenuItem simplifyNameInTabTitle = new JCheckBoxMenuItem("Simplify Name In Tab Title");
public final JCheckBoxMenuItem forcePureAsciiAsText = new JCheckBoxMenuItem("Force Pure Ascii As Text"); public final JCheckBoxMenuItem forcePureAsciiAsText = new JCheckBoxMenuItem("Force Pure Ascii As Text");
public final JCheckBoxMenuItem autoCompileOnRefresh = new JCheckBoxMenuItem("Compile On Refresh"); public final JCheckBoxMenuItem autoCompileOnRefresh = new JCheckBoxMenuItem("Compile On Refresh");
public final JCheckBoxMenuItem decodeAPKResources = new JCheckBoxMenuItem("Decode APK Resources"); public final JCheckBoxMenuItem decodeAPKResources = new JCheckBoxMenuItem("Decode APK Resources");
public final JCheckBoxMenuItem synchronizedViewing = new JCheckBoxMenuItem("Synchronized Viewing"); public final JCheckBoxMenuItem synchronizedViewing = new JCheckBoxMenuItem("Synchronized Viewing");
public final JCheckBoxMenuItem showClassMethods = new JCheckBoxMenuItem("Show Class Methods"); public final JCheckBoxMenuItem showClassMethods = new JCheckBoxMenuItem("Show Class Methods");
public final Map<RSTATheme, JRadioButtonMenuItem> rstaThemes = new HashMap<>();
public final Map<LAFTheme, JRadioButtonMenuItem> lafThemes = new HashMap<>();
//CFIDE settings //CFIDE settings
public final JCheckBoxMenuItem appendBracketsToLabels = new JCheckBoxMenuItem("Append Brackets To Labels"); public final JCheckBoxMenuItem appendBracketsToLabels = new JCheckBoxMenuItem("Append Brackets To Labels");
public JCheckBoxMenuItem debugHelpers = new JCheckBoxMenuItem("Debug Helpers"); public JCheckBoxMenuItem debugHelpers = new JCheckBoxMenuItem("Debug Helpers");
@ -384,6 +382,7 @@ public class MainViewerGUI extends JFrame {
public void buildViewMenu() public void buildViewMenu()
{ {
rootMenu.add(viewMainMenu); rootMenu.add(viewMainMenu);
viewMainMenu.add(visualSettings);
viewMainMenu.add(viewPane1.menu); viewMainMenu.add(viewPane1.menu);
viewMainMenu.add(viewPane2.menu); viewMainMenu.add(viewPane2.menu);
viewMainMenu.add(viewPane3.menu); viewMainMenu.add(viewPane3.menu);
@ -393,8 +392,8 @@ public class MainViewerGUI extends JFrame {
{ {
rootMenu.add(settingsMainMenu); rootMenu.add(settingsMainMenu);
settingsMainMenu.add(visualSettings); //settingsMainMenu.add(visualSettings);
settingsMainMenu.add(new JSeparator()); //settingsMainMenu.add(new JSeparator());
settingsMainMenu.add(compileOnSave); settingsMainMenu.add(compileOnSave);
settingsMainMenu.add(autoCompileOnRefresh); settingsMainMenu.add(autoCompileOnRefresh);
settingsMainMenu.add(refreshOnChange); settingsMainMenu.add(refreshOnChange);
@ -420,8 +419,54 @@ public class MainViewerGUI extends JFrame {
fontSpinner.setSize(new Dimension(42, 20)); fontSpinner.setSize(new Dimension(42, 20));
fontSpinner.setModel(new SpinnerNumberModel(12, 1, null, 1)); fontSpinner.setModel(new SpinnerNumberModel(12, 1, null, 1));
fontSize.add(fontSpinner); fontSize.add(fontSpinner);
ButtonGroup rstaGroup = new ButtonGroup();
for (RSTATheme t : RSTATheme.values())
{
JRadioButtonMenuItem item = new JRadioButtonMenuItem(t.getReadableName());
if (Configuration.rstaTheme.equals(t))
item.setSelected(true);
rstaGroup.add(item);
item.addActionListener(e ->
{
Configuration.rstaTheme = t;
item.setSelected(true);
Settings.saveSettings();
});
rstaThemes.put(t, item);
rstaTheme.add(item);
}
ButtonGroup lafGroup = new ButtonGroup();
for (LAFTheme theme : LAFTheme.values())
{
JRadioButtonMenuItem item = new JRadioButtonMenuItem(theme.getReadableName());
if (Configuration.lafTheme.equals(theme))
item.setSelected(true);
lafGroup.add(item);
item.addActionListener(e ->
{
Configuration.lafTheme = theme;
Configuration.rstaTheme = theme.getRSTATheme();
rstaThemes.get(Configuration.rstaTheme).setSelected(true);
item.setSelected(true);
Settings.saveSettings();
});
lafThemes.put(theme, item);
lafTheme.add(item);
}
visualSettings.add(lafTheme);
visualSettings.add(rstaTheme);
visualSettings.add(fontSize); visualSettings.add(fontSize);
visualSettings.add(showFileInTabTitle); visualSettings.add(showFileInTabTitle);
visualSettings.add(simplifyNameInTabTitle);
visualSettings.add(synchronizedViewing); visualSettings.add(synchronizedViewing);
visualSettings.add(showClassMethods); visualSettings.add(showClassMethods);
@ -527,6 +572,10 @@ public class MainViewerGUI extends JFrame {
Configuration.displayParentInTab = BytecodeViewer.viewer.showFileInTabTitle.isSelected(); Configuration.displayParentInTab = BytecodeViewer.viewer.showFileInTabTitle.isSelected();
Settings.saveSettings(); Settings.saveSettings();
}); });
simplifyNameInTabTitle.addActionListener(arg0 -> {
Configuration.simplifiedTabNames = BytecodeViewer.viewer.simplifyNameInTabTitle.isSelected();
Settings.saveSettings();
});
} }
public void buildPluginMenu() public void buildPluginMenu()

View file

@ -1,352 +0,0 @@
package the.bytecode.club.bytecodeviewer.gui;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.*;
import javax.swing.plaf.basic.BasicButtonUI;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* Component to be used as tabComponent; Contains a JLabel to show the text and a JButton to close the tab it belongs to
*
* @author Konloch
* @author WaterWolf
*/
public class TabbedPane extends JPanel {
private static final long serialVersionUID = -4774885688297538774L;
public static final Color BLANK = new Color(0, 0, 0, 0);
private final JTabbedPane pane;
public final JLabel label;
private DelayTabbedPaneThread probablyABadIdea;
private long startedDragging = 0;
private static long zero = System.currentTimeMillis();
public String tabName;
public String fileContainerName;
public TabbedPane(String fileContainerName, String name, final JTabbedPane pane) {
// unset default FlowLayout' gaps
super(new FlowLayout(FlowLayout.LEFT, 0, 0));
this.tabName = name;
this.fileContainerName = fileContainerName;
if (pane == null)
throw new NullPointerException("TabbedPane is null");
this.pane = pane;
setOpaque(false);
// make JLabel read titles from JTabbedPane
label = new JLabel() {
private static final long serialVersionUID = -5511025206527893360L;
@Override
public String getText() {
final int i = pane.indexOfTabComponent(TabbedPane.this);
if (i != -1)
return pane.getTitleAt(i);
return null;
}
@Override
public Dimension getPreferredSize() {
Dimension realDimension = super.getPreferredSize();
if (realDimension.getWidth() >= 400)
return new Dimension(400, 20);
else
return realDimension;
}
};
this.add(label);
// add more space between the label and the button
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
// tab button
JButton button = new TabButton();
this.add(button);
// add more space to the top of the component
setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
JPopupMenu pop_up = new JPopupMenu();
JMenuItem closealltab = new JMenuItem("Close All But This: " + name);
JMenuItem closetab = new JMenuItem("Close Tab: " + name);
closetab.addActionListener(e -> {
String name1 = e.getActionCommand().split(": ")[1];
final int i = pane.indexOfTab(name1);
if (i != -1)
pane.remove(i);
});
closealltab.addActionListener(e -> {
String name12 = e.getActionCommand().split(": ")[1];
while (true) {
int thisID = pane.indexOfTab(name12);
if (pane.getTabCount() <= 1) {
return;
}
if (thisID != 0)
pane.remove(0);
else
pane.remove(1);
}
});
pop_up.add(closealltab);
pop_up.add(closetab);
//setComponentPopupMenu(pop_up);
button.setComponentPopupMenu(pop_up);
button.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getModifiers() == InputEvent.ALT_MASK) {
if (System.currentTimeMillis() - zero >= 100) {
zero = System.currentTimeMillis();
final int i = pane.indexOfTabComponent(TabbedPane.this);
if (i != -1)
pane.remove(i);
}
}
}
@Override
public void mouseEntered(MouseEvent arg0) {
}
@Override
public void mouseExited(MouseEvent arg0) {
}
@Override
public void mousePressed(MouseEvent arg0) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
});
//tab dragging
if(BytecodeViewer.EXPERIMENTAL_TAB_CODE)
{
/*label.addMouseListener(new MouseListener() {
@Override public void mouseClicked(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent arg0) {
}
@Override public void mouseExited(MouseEvent arg0) {
}
@Override public void mousePressed(MouseEvent e) {
onMousePressed(e);
}
@Override public void mouseReleased(MouseEvent e) {
stopDragging(e.getX(), e.getY());
}
});*/
this.addMouseListener(new MouseListener() {
@Override public void mouseClicked(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent arg0) {
}
@Override public void mouseExited(MouseEvent arg0) {
}
@Override public void mousePressed(MouseEvent e) {
onMousePressed(e);
}
@Override public void mouseReleased(MouseEvent e) {
stopDragging(e.getX(), e.getY());
}
});
}
//middle click close
if(BytecodeViewer.EXPERIMENTAL_TAB_CODE)
{
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON2) {
final int i = pane.indexOfTabComponent(TabbedPane.this);
if (i != -1) {
pane.remove(i);
}
}
}
});
}
}
private void stopDragging(int mouseX, int mouseY) {
if (System.currentTimeMillis() - startedDragging >= 210) {
Rectangle bounds = new Rectangle(1, 1, mouseX, mouseY);
System.out.println("debug-5: " + mouseX + ", " + mouseY);
int totalTabs = BytecodeViewer.viewer.workPane.tabs.getTabCount();
int index = -1;
for (int i = 0; i < totalTabs; i++) {
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if (c != null && bounds.intersects(c.getBounds())) {
index = i; //replace this tabs position
}
}
if (index == -1) {
for (int i = 0; i < totalTabs; i++) {
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
//do some check to see if it's past the X or Y
if (c != null) {
System.out.println("debug-6: " + c.getBounds());
}
}
}
if (index != -1) {
BytecodeViewer.viewer.workPane.tabs.remove(this);
BytecodeViewer.viewer.workPane.tabs.setTabComponentAt(index, this);
}
}
SwingUtilities.invokeLater(() ->
{
label.setBackground(BLANK);
label.updateUI();
});
}
public void onMousePressed(MouseEvent e)
{
BytecodeViewer.viewer.workPane.tabs.dispatchEvent(e);
if(e.getButton() == 1)
{
startedDragging = System.currentTimeMillis();
//dragging = true;
if (probablyABadIdea != null)
{
probablyABadIdea.stopped = true;
}
probablyABadIdea = new DelayTabbedPaneThread(TabbedPane.this);
probablyABadIdea.start();
repaint();
System.out.println(e.getX()+", "+e.getY());
Rectangle bounds = new Rectangle(1, 1, e.getX(), e.getY());
for(int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++)
{
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if(c != null && bounds.intersects(c.getBounds()))
{
BytecodeViewer.viewer.workPane.tabs.setSelectedIndex(i);
}
}
}
else
{
stopDragging(e.getX(), e.getY());
}
}
private class TabButton extends JButton implements ActionListener {
private static final long serialVersionUID = -4492967978286454159L;
public TabButton() {
final int size = 17;
setPreferredSize(new Dimension(size, size));
setToolTipText("Close this tab");
// Make the button looks the same for all Laf's
setUI(new BasicButtonUI());
// Make it transparent
setContentAreaFilled(false);
// No need to be focusable
setFocusable(false);
setBorder(BorderFactory.createEtchedBorder());
setBorderPainted(false);
// Making nice rollover effect
// we use the same listener for all buttons
addMouseListener(buttonMouseListener);
setRolloverEnabled(true);
// Close the proper tab by clicking the button
addActionListener(this);
}
@Override
public void actionPerformed(final ActionEvent e) {
final int i = pane.indexOfTabComponent(TabbedPane.this);
if (i != -1) {
pane.remove(i);
}
}
// we don't want to update UI for this button
@Override
public void updateUI() {
}
// paint the cross
@Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g.create();
// shift the image for pressed buttons
if (getModel().isPressed()) {
g2.translate(1, 1);
}
g2.setStroke(new BasicStroke(2));
g2.setColor(Color.BLACK);
if (getModel().isRollover()) {
g2.setColor(Color.MAGENTA);
}
final int delta = 6;
g2.drawLine(delta, delta, getWidth() - delta - 1, getHeight()
- delta - 1);
g2.drawLine(getWidth() - delta - 1, delta, delta, getHeight()
- delta - 1);
g2.dispose();
}
}
private final static MouseListener buttonMouseListener = new MouseAdapter() {
@Override
public void mouseEntered(final MouseEvent e) {
final Component component = e.getComponent();
if (component instanceof AbstractButton) {
final AbstractButton button = (AbstractButton) component;
button.setBorderPainted(true);
}
}
@Override
public void mouseExited(final MouseEvent e) {
final Component component = e.getComponent();
if (component instanceof AbstractButton) {
final AbstractButton button = (AbstractButton) component;
button.setBorderPainted(false);
}
}
};
}

View file

@ -1,294 +0,0 @@
package the.bytecode.club.bytecodeviewer.gui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashMap;
import javax.swing.JButton;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.util.FileContainer;
import static the.bytecode.club.bytecodeviewer.Constants.BLOCK_TAB_MENU;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* The pane that contains all of the classes as tabs.
*
* @author Konloch
* @author WaterWolf
*/
public class WorkPane extends VisibleComponent implements ActionListener {
private static final long serialVersionUID = 6542337997679487946L;
public JTabbedPane tabs;
JPanel buttonPanel;
public JButton refreshClass;
HashMap<String, Integer> workingOn = new HashMap<>();
public WorkPane() {
super("WorkPanel");
setTitle("Work Space");
this.tabs = new JTabbedPane();
JPopupMenu pop_up = new JPopupMenu() {
@Override
public void setVisible(boolean b) {
super.setVisible(b);
}
};
JMenuItem closealltab = new JMenuItem("Close All But This");
JMenuItem closetab = new JMenuItem("Close Tab");
closetab.addActionListener(e -> {
/*String name = e.getActionCommand().split(": ")[1];
final int i = pane.indexOfTab(name);
if (i != -1)
pane.remove(i);*/
});
closealltab.addActionListener(e -> {
String name = e.getActionCommand().split(": ")[1];
System.out.println("debug-3: " + name);
while (true) {
int thisID = tabs.indexOfTab(name);
if (tabs.getTabCount() <= 1) {
return;
}
if (thisID != 0)
tabs.remove(0);
else
tabs.remove(1);
}
});
tabs.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent arg0) {
}
@Override
public void mouseExited(MouseEvent arg0) {
}
@Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == 3) {
if (BLOCK_TAB_MENU)
return;
Rectangle bounds = new Rectangle(1, 1, e.getX(), e.getY());
Point point = tabs.getMousePosition();
System.out.println("debug-1: " + point);
for (int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++) {
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if (c != null && bounds.intersects(c.getBounds())) {
pop_up.setVisible(true);
closealltab.setText("Close All But This: " + ((TabbedPane) c).tabName);
closetab.setText("Close Tab: " + ((TabbedPane) c).tabName);
//do something with this shit
//BytecodeViewer.viewer.workPane.tabs.setSelectedIndex(i);
} else {
pop_up.setVisible(false);
}
}
System.out.println("debug-2: " + e.getX() + ", " + e.getY());
}
}
@Override
public void mouseReleased(MouseEvent e) {
}
});
pop_up.add(closealltab);
pop_up.add(closetab);
if (!BLOCK_TAB_MENU)
tabs.setComponentPopupMenu(pop_up);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(tabs, BorderLayout.CENTER);
buttonPanel = new JPanel(new FlowLayout());
refreshClass = new JButton("Refresh");
refreshClass.addActionListener(this);
buttonPanel.add(refreshClass);
buttonPanel.setVisible(false);
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
tabs.addContainerListener(new ContainerListener() {
@Override
public void componentAdded(final ContainerEvent e) {
}
@Override
public void componentRemoved(final ContainerEvent e) {
final Component c = e.getChild();
if (c instanceof ClassViewer) {
String containerName = ((ClassViewer) c).container.name + ">";
String fileName = ((ClassViewer) c).name;
if (fileName.startsWith(containerName)) {
workingOn.remove(fileName);
} else {
workingOn.remove(containerName + fileName);
}
}
if (c instanceof FileViewer) {
String containerName = ((FileViewer) c).container.name + ">";
String fileName = ((FileViewer) c).name;
if (fileName.startsWith(containerName)) {
workingOn.remove(fileName);
} else {
workingOn.remove(containerName + fileName);
}
}
}
});
tabs.addChangeListener(arg0 -> buttonPanel.setVisible(tabs.getSelectedIndex() != -1));
this.setVisible(true);
}
public void addWorkingFile(final FileContainer container, String name, final ClassNode cn) {
String workingName = container.name + ">" + name;
String containerName = name;
if (Configuration.displayParentInTab)
containerName = container.name + ">" + name;
if (!workingOn.containsKey(workingName)) {
final ClassViewer tabComp = new ClassViewer(container, containerName, cn);
tabs.add(tabComp);
final int tabCount = tabs.indexOfComponent(tabComp);
workingOn.put(workingName, tabCount);
TabbedPane tabbedPane = new TabbedPane(container.name, name, tabs);
tabComp.tabbedPane = tabbedPane;
tabs.setTabComponentAt(tabCount, tabbedPane);
tabs.setSelectedIndex(tabCount);
} else {
tabs.setSelectedIndex(workingOn.get(workingName));
}
}
public void addFile(final FileContainer container, String name, byte[] contents) {
String workingName = container.name + ">" + name;
if (Configuration.displayParentInTab)
name = container.name + ">" + name;
if (contents == null) //a directory
return;
if (!workingOn.containsKey(workingName)) {
final FileViewer tabComp = new FileViewer(container, name, contents);
tabs.add(tabComp);
final int tabCount = tabs.indexOfComponent(tabComp);
workingOn.put(workingName, tabCount);
TabbedPane tabbedPane = new TabbedPane(null, name, tabs);
tabComp.tabbedPane = tabbedPane;
tabs.setTabComponentAt(tabCount, tabbedPane);
tabs.setSelectedIndex(tabCount);
} else {
try {
tabs.setSelectedIndex(workingOn.get(workingName));
} catch (Exception e) {
//workingOn.remove(workingName);
e.printStackTrace();
}
}
}
public Viewer getCurrentViewer() {
return (Viewer) tabs.getSelectedComponent();
}
public java.awt.Component[] getLoadedViewers() {
return tabs.getComponents();
}
@Override
public void actionPerformed(final ActionEvent arg0) {
Thread t = new Thread(() -> {
if (BytecodeViewer.viewer.autoCompileOnRefresh.isSelected())
try {
if (!BytecodeViewer.compile(false))
return;
} catch (NullPointerException ignored) {
}
final JButton src = (JButton) arg0.getSource();
if (src == refreshClass) {
final Component tabComp = tabs.getSelectedComponent();
if (tabComp != null) {
if (tabComp instanceof ClassViewer) {
src.setEnabled(false);
BytecodeViewer.viewer.updateBusyStatus(true);
((ClassViewer) tabComp).startPaneUpdater(src);
BytecodeViewer.viewer.updateBusyStatus(false);
} else if (tabComp instanceof FileViewer) {
src.setEnabled(false);
BytecodeViewer.viewer.updateBusyStatus(true);
((FileViewer) tabComp).refresh(src);
BytecodeViewer.viewer.updateBusyStatus(false);
}
}
}
});
t.start();
}
public void resetWorkspace() {
tabs.removeAll();
tabs.updateUI();
}
}

View file

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui.extras; package the.bytecode.club.bytecodeviewer.gui.components;
import java.awt.CardLayout; import java.awt.CardLayout;
import java.awt.Color; import java.awt.Color;

View file

@ -0,0 +1,35 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
/**
* @author Konloch
* @since 6/25/2021
*/
public class ButtonHoverAnimation extends MouseAdapter
{
@Override
public void mouseEntered(final MouseEvent e)
{
final Component component = e.getComponent();
if (component instanceof AbstractButton)
{
final AbstractButton button = (AbstractButton) component;
button.setBorderPainted(true);
}
}
@Override
public void mouseExited(final MouseEvent e)
{
final Component component = e.getComponent();
if (component instanceof AbstractButton)
{
final AbstractButton button = (AbstractButton) component;
button.setBorderPainted(false);
}
}
}

View file

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui; package the.bytecode.club.bytecodeviewer.gui.components;
import the.bytecode.club.bytecodeviewer.util.RefreshWorkPane; import the.bytecode.club.bytecodeviewer.util.RefreshWorkPane;
@ -33,6 +33,7 @@ public class DecompilerViewComponent
menu.add(java); menu.add(java);
if(hasBytecodeOption) if(hasBytecodeOption)
menu.add(bytecode); menu.add(bytecode);
menu.add(new JSeparator()); menu.add(new JSeparator());
menu.add(editable); menu.add(editable);

View file

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui.extras; package the.bytecode.club.bytecodeviewer.gui.components;
import java.awt.Dimension; import java.awt.Dimension;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;

View file

@ -0,0 +1,18 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import javax.swing.*;
import java.awt.*;
/**
* Display an image on a JLabel element
*
* @author Konloch
* @since 6/25/2021
*/
public class ImageJLabel extends JLabel
{
public ImageJLabel(Image image)
{
super("", new ImageIcon(image), JLabel.CENTER);
}
}

View file

@ -0,0 +1,59 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import the.bytecode.club.bytecodeviewer.Resources;
import javax.swing.*;
import java.awt.*;
/**
* A simple swing JFrame console
*
* @author Konloch
* @since 6/25/2021
*/
public class JFrameConsole extends JFrame
{
private final SearchableJTextArea textArea = new SearchableJTextArea();
public JFrameConsole(String title)
{
this.setIconImages(Resources.iconList);
setTitle(title);
setSize(new Dimension(542, 316));
getContentPane().add(textArea.getScrollPane(), BorderLayout.CENTER);
this.setLocationRelativeTo(null);
}
/**
* Appends \r\n to the end of your string, then it puts it on the top.
*
* @param t the string you want to append
*/
public void appendText(String t)
{
setText((textArea.getText().isEmpty()
? ""
: textArea.getText() + "\r\n"
) + t);
}
/**
* Sets the text
*
* @param t the text you want set
*/
public void setText(String t)
{
textArea.setText(t);
textArea.setCaretPosition(0);
}
public SearchableJTextArea getTextArea()
{
return textArea;
}
private static final long serialVersionUID = -5056940543411437508L;
}

View file

@ -0,0 +1,62 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import java.io.PrintStream;
import static the.bytecode.club.bytecodeviewer.Constants.nl;
/**
* A swing console that can print out from PrintStreams
*
* @author Konloch
* @since 6/25/2021
*/
public class JFrameConsolePrintStream extends JFrameConsole
{
private final PrintStream originalOut;
private final PrintStream newPrintStream;
private final JTextAreaOutputStream s;
public JFrameConsolePrintStream(String title, PrintStream originalOut)
{
super(title);
this.originalOut = originalOut;
s = new JTextAreaOutputStream(getTextArea());
newPrintStream = new PrintStream(s);
}
public void finished()
{
if (originalOut != null)
System.setErr(originalOut);
}
public PrintStream getNewPrintStream()
{
return newPrintStream;
}
public void pretty()
{
s.update();
String[] test;
if (getTextArea().getText().split("\n").length >= 2)
test = getTextArea().getText().split("\n");
else
test = getTextArea().getText().split("\r");
StringBuilder replace = new StringBuilder();
for (String s : test)
{
if (s.startsWith("File '"))
{
String[] split = s.split("'");
String start = split[0] + "'" + split[1] + "', ";
s = s.substring(start.length());
}
replace.append(s).append(nl);
}
setText(replace.toString());
}
}

View file

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui.extras; package the.bytecode.club.bytecodeviewer.gui.components;
import javax.swing.*; import javax.swing.*;
import java.io.IOException; import java.io.IOException;

View file

@ -0,0 +1,33 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import javax.swing.*;
import java.awt.*;
/**
* @author Konloch
* @since 6/25/2021
*/
public class MaxWidthJLabel extends JLabel
{
private final int width;
private final int height;
public MaxWidthJLabel(String title, int width, int height)
{
super(title);
this.width = width;
this.height = height;
}
@Override
public Dimension getPreferredSize()
{
Dimension realDimension = super.getPreferredSize();
if (realDimension.getWidth() >= width)
return new Dimension(width, height);
else
return realDimension;
}
private static final long serialVersionUID = -5511025206527893360L;
}

View file

@ -0,0 +1,33 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import the.bytecode.club.bytecodeviewer.gui.util.PaneUpdaterThread;
import the.bytecode.club.bytecodeviewer.util.MethodParser;
import javax.swing.*;
import java.awt.*;
/**
* @author Konloch
* @author Waterwolf
* @since 6/24/2021
*/
public class MethodsRenderer extends JLabel implements ListCellRenderer<Object>
{
private final PaneUpdaterThread paneUpdaterThread;
public MethodsRenderer(PaneUpdaterThread paneUpdaterThread)
{
this.paneUpdaterThread = paneUpdaterThread;
setOpaque(true);
}
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected,
boolean cellHasFocus)
{
MethodParser methods = paneUpdaterThread.viewer.methods.get(paneUpdaterThread.decompilerViewIndex);
MethodParser.Method method = methods.getMethod((Integer) value);
setText(method.toString());
return this;
}
}

View file

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui.extras; package the.bytecode.club.bytecodeviewer.gui.components;
import java.awt.Dimension; import java.awt.Dimension;
import javax.swing.JButton; import javax.swing.JButton;

View file

@ -0,0 +1,92 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Resources;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.PressKeyListener;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.ReleaseKeyListener;
import the.bytecode.club.bytecodeviewer.util.JTextAreaUtils;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
/**
* Searching on a JTextArea using swing highlighting
*
* @author Konloch
* @since 6/25/2021
*/
public class SearchableJTextArea extends JTextArea
{
private final JScrollPane scrollPane = new JScrollPane();
private final JPanel searchPanel = new JPanel(new BorderLayout());
private final JTextField searchInput = new JTextField();
private final JCheckBox caseSensitiveSearch = new JCheckBox("Exact");
public SearchableJTextArea()
{
scrollPane.setViewportView(this);
scrollPane.setColumnHeaderView(searchPanel);
JButton searchNext = new JButton();
searchNext.setIcon(Resources.nextIcon);
JButton searchPrev = new JButton();
searchPrev.setIcon(Resources.prevIcon);
JPanel buttonPane = new JPanel(new BorderLayout());
buttonPane.add(searchNext, BorderLayout.WEST);
buttonPane.add(searchPrev, BorderLayout.EAST);
searchPanel.add(buttonPane, BorderLayout.WEST);
searchPanel.add(searchInput, BorderLayout.CENTER);
searchPanel.add(caseSensitiveSearch, BorderLayout.EAST);
searchNext.addActionListener(arg0 -> search(searchInput.getText(), true, caseSensitiveSearch.isSelected()));
searchPrev.addActionListener(arg0 -> search(searchInput.getText(), false, caseSensitiveSearch.isSelected()));
searchInput.addKeyListener(new ReleaseKeyListener(keyEvent ->
{
if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER)
search(searchInput.getText(), true, caseSensitiveSearch.isSelected());
}));
addKeyListener(new PressKeyListener(keyEvent ->
{
if ((keyEvent.getKeyCode() == KeyEvent.VK_F) && ((keyEvent.getModifiers() & KeyEvent.CTRL_MASK) != 0))
searchInput.requestFocus();
BytecodeViewer.checkHotKey(keyEvent);
}));
}
public void search(String search, boolean forwardSearchDirection, boolean caseSensitiveSearch)
{
JTextAreaUtils.search(this, search, forwardSearchDirection, caseSensitiveSearch);
}
public void highlight(String pattern, boolean caseSensitiveSearch)
{
JTextAreaUtils.highlight(this, pattern, caseSensitiveSearch);
}
public JScrollPane getScrollPane()
{
return scrollPane;
}
public JPanel getSearchPanel()
{
return searchPanel;
}
public JTextField getSearchInput()
{
return searchInput;
}
public JCheckBox getCaseSensitiveSearch()
{
return caseSensitiveSearch;
}
}

View file

@ -0,0 +1,93 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rtextarea.RTextScrollPane;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Resources;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.PressKeyListener;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.ReleaseKeyListener;
import the.bytecode.club.bytecodeviewer.util.JTextAreaUtils;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
/**
* Searching on an RSyntaxTextArea using swing highlighting
*
* @author Konloch
* @since 6/25/2021
*/
public class SearchableRSyntaxTextArea extends RSyntaxTextArea
{
private final RTextScrollPane scrollPane = new RTextScrollPane(this);
private final JPanel searchPanel = new JPanel(new BorderLayout());
private final JTextField searchInput = new JTextField();
private final JCheckBox caseSensitiveSearch = new JCheckBox("Exact");
public SearchableRSyntaxTextArea()
{
setAntiAliasingEnabled(true);
scrollPane.setColumnHeaderView(searchPanel);
JButton searchNext = new JButton();
JButton searchPrev = new JButton();
JPanel buttonPane = new JPanel(new BorderLayout());
buttonPane.add(searchNext, BorderLayout.WEST);
buttonPane.add(searchPrev, BorderLayout.EAST);
searchNext.setIcon(Resources.nextIcon);
searchPrev.setIcon(Resources.prevIcon);
searchPanel.add(buttonPane, BorderLayout.WEST);
searchPanel.add(searchInput, BorderLayout.CENTER);
searchPanel.add(caseSensitiveSearch, BorderLayout.EAST);
searchNext.addActionListener(arg0 -> search(searchInput.getText(), true, caseSensitiveSearch.isSelected()));
searchPrev.addActionListener(arg0 -> search(searchInput.getText(), false, caseSensitiveSearch.isSelected()));
searchInput.addKeyListener(new ReleaseKeyListener(keyEvent ->
{
System.out.println("DEBUG: " + keyEvent.getKeyChar());
if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER)
search(searchInput.getText(), true, caseSensitiveSearch.isSelected());
}));
addKeyListener(new PressKeyListener(keyEvent ->
{
if ((keyEvent.getKeyCode() == KeyEvent.VK_F) && ((keyEvent.getModifiers() & KeyEvent.CTRL_MASK) != 0))
searchInput.requestFocus();
BytecodeViewer.checkHotKey(keyEvent);
}));
}
public void search(String search, boolean forwardSearchDirection, boolean caseSensitiveSearch)
{
JTextAreaUtils.search(this, search, forwardSearchDirection, caseSensitiveSearch);
}
public void highlight(String pattern, boolean caseSensitiveSearch)
{
JTextAreaUtils.highlight(this, pattern, caseSensitiveSearch);
}
public RTextScrollPane getScrollPane()
{
return scrollPane;
}
public JPanel getSearchPanel()
{
return searchPanel;
}
public JTextField getSearchInput()
{
return searchInput;
}
public JCheckBox getCaseSensitiveSearch()
{
return caseSensitiveSearch;
}
}

View file

@ -0,0 +1,57 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.PrintStream;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Resources;
import static the.bytecode.club.bytecodeviewer.Constants.*;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* A simple console GUI.
*
* @author Konloch
*/
public class SystemErrConsole extends JFrameConsolePrintStream
{
public SystemErrConsole(String title)
{
super(title, System.err);
System.setErr(getNewPrintStream());
}
private static final long serialVersionUID = -6556940545421437508L;
}

View file

@ -0,0 +1,36 @@
package the.bytecode.club.bytecodeviewer.gui.components;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* A simple console GUI.
*
* @author Konloch
*/
public class SystemOutConsole extends JFrameConsolePrintStream
{
public SystemOutConsole(String title)
{
super(title, System.out);
System.setOut(getNewPrintStream());
}
private static final long serialVersionUID = -6666940545499937508L;
}

View file

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui; package the.bytecode.club.bytecodeviewer.gui.components;
import javax.swing.JInternalFrame; import javax.swing.JInternalFrame;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;

View file

@ -0,0 +1,36 @@
package the.bytecode.club.bytecodeviewer.gui.components.listeners;
import java.awt.event.*;
/**
* @author Konloch
* @since 6/25/2021
*/
public class MouseClickedListener implements MouseListener
{
private final MouseClickedEvent mouseClickedEvent;
public MouseClickedListener(MouseClickedEvent mouseClickedEvent) {this.mouseClickedEvent = mouseClickedEvent;}
@Override
public void mouseClicked(MouseEvent e) {
mouseClickedEvent.mouseClicked(e);
}
@Override
public void mouseEntered(MouseEvent arg0) { }
@Override
public void mouseExited(MouseEvent arg0) { }
@Override
public void mousePressed(MouseEvent arg0) { }
@Override
public void mouseReleased(MouseEvent e) { }
public interface MouseClickedEvent
{
void mouseClicked(MouseEvent e);
}
}

View file

@ -0,0 +1,32 @@
package the.bytecode.club.bytecodeviewer.gui.components.listeners;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
* @author Konloch
* @since 6/25/2021
*/
public class PressKeyListener implements KeyListener
{
private final KeyPressedEvent keyPressedEvent;
public PressKeyListener(KeyPressedEvent keyPressedEvent) {this.keyPressedEvent = keyPressedEvent;}
@Override
public void keyTyped(KeyEvent e) { }
@Override
public void keyPressed(KeyEvent e)
{
keyPressedEvent.keyReleased(e);
}
@Override
public void keyReleased(KeyEvent e) {}
public interface KeyPressedEvent
{
void keyReleased(KeyEvent e);
}
}

View file

@ -0,0 +1,32 @@
package the.bytecode.club.bytecodeviewer.gui.components.listeners;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
* @author Konloch
* @since 6/25/2021
*/
public class ReleaseKeyListener implements KeyListener
{
private final KeyReleasedEvent keyReleasedEvent;
public ReleaseKeyListener(KeyReleasedEvent keyReleasedEvent) {this.keyReleasedEvent = keyReleasedEvent;}
@Override
public void keyTyped(KeyEvent e) { }
@Override
public void keyPressed(KeyEvent e) { }
@Override
public void keyReleased(KeyEvent e)
{
keyReleasedEvent.keyReleased(e);
}
public interface KeyReleasedEvent
{
void keyReleased(KeyEvent e);
}
}

View file

@ -1,298 +0,0 @@
package the.bytecode.club.bytecodeviewer.gui.extras;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.PrintStream;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Resources;
import static the.bytecode.club.bytecodeviewer.Constants.*;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* A simple console GUI.
*
* @author Konloch
*/
public class SystemErrConsole extends JFrame
{
private final JTextArea textArea = new JTextArea();
private final JPanel panel = new JPanel(new BorderLayout());
private final JScrollPane scrollPane = new JScrollPane();
private final JCheckBox check = new JCheckBox("Exact");
private final JTextField field = new JTextField();
private final PrintStream originalOut;
private final JTextAreaOutputStream s;
public SystemErrConsole(String title)
{
this.setIconImages(Resources.iconList);
setTitle(title);
setSize(new Dimension(542, 316));
getContentPane().add(scrollPane, BorderLayout.CENTER);
scrollPane.setViewportView(textArea);
textArea.addKeyListener(new KeyListener()
{
@Override
public void keyPressed(KeyEvent e) {
if ((e.getKeyCode() == KeyEvent.VK_F) && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
field.requestFocus();
}
BytecodeViewer.checkHotKey(e);
}
@Override
public void keyReleased(KeyEvent arg0) {
}
@Override
public void keyTyped(KeyEvent arg0) {
}
});
JButton searchNext = new JButton();
JButton searchPrev = new JButton();
JPanel buttonPane = new JPanel(new BorderLayout());
buttonPane.add(searchNext, BorderLayout.WEST);
buttonPane.add(searchPrev, BorderLayout.EAST);
searchNext.setIcon(Resources.nextIcon);
searchPrev.setIcon(Resources.prevIcon);
panel.add(buttonPane, BorderLayout.WEST);
panel.add(field, BorderLayout.CENTER);
panel.add(check, BorderLayout.EAST);
searchNext.addActionListener(arg0 -> search(field.getText(), true));
searchPrev.addActionListener(arg0 -> search(field.getText(), false));
field.addKeyListener(new KeyListener() {
@Override
public void keyReleased(KeyEvent arg0) {
if (arg0.getKeyCode() == KeyEvent.VK_ENTER)
search(field.getText(), true);
}
@Override
public void keyPressed(KeyEvent arg0) {
}
@Override
public void keyTyped(KeyEvent arg0) {
}
});
scrollPane.setColumnHeaderView(panel);
this.setLocationRelativeTo(null);
s = new JTextAreaOutputStream(textArea);
PrintStream printStream = new PrintStream(s);
originalOut = System.err;
System.setErr(printStream);
}
public void finished() {
if (originalOut != null)
System.setErr(originalOut);
}
public void pretty() {
s.update();
String[] test;
if (textArea.getText().split("\n").length >= 2)
test = textArea.getText().split("\n");
else
test = textArea.getText().split("\r");
StringBuilder replace = new StringBuilder();
for (String s : test) {
if (s.startsWith("File '")) {
String[] split = s.split("'");
String start = split[0] + "'" + split[1] + "', ";
s = s.substring(start.length());
}
replace.append(s).append(nl);
}
setText(replace.toString());
}
/**
* This was really interesting to write.
*
* @author Konloch
*/
public void search(String search, boolean next) {
try {
JTextArea area = textArea;
if (search.isEmpty()) {
highlight(area, "");
return;
}
int startLine = area.getDocument().getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1;
int currentLine = 1;
boolean canSearch = false;
String[] test;
if (area.getText().split("\n").length >= 2)
test = area.getText().split("\n");
else
test = area.getText().split("\r");
int lastGoodLine = -1;
int firstPos = -1;
boolean found = false;
if (next) {
for (String s : test) {
if (!check.isSelected()) {
s = s.toLowerCase();
search = search.toLowerCase();
}
if (currentLine == startLine) {
canSearch = true;
} else if (s.contains(search)) {
if (canSearch) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(currentLine - 1)
.getStartOffset());
canSearch = false;
found = true;
}
if (firstPos == -1)
firstPos = currentLine;
}
currentLine++;
}
if (!found && firstPos != -1) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement().getElement(firstPos - 1)
.getStartOffset());
}
} else {
canSearch = true;
for (String s : test) {
if (!check.isSelected()) {
s = s.toLowerCase();
search = search.toLowerCase();
}
if (s.contains(search)) {
if (lastGoodLine != -1 && canSearch)
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1)
.getStartOffset());
lastGoodLine = currentLine;
if (currentLine >= startLine)
canSearch = false;
}
currentLine++;
}
if (lastGoodLine != -1
&& area.getDocument().getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1 == startLine) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1).getStartOffset());
}
}
highlight(area, search);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
private final DefaultHighlighter.DefaultHighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(
new Color(255, 62, 150));
public void highlight(JTextComponent textComp, String pattern) {
if (pattern.isEmpty()) {
textComp.getHighlighter().removeAllHighlights();
return;
}
try {
Highlighter hilite = textComp.getHighlighter();
hilite.removeAllHighlights();
javax.swing.text.Document doc = textComp.getDocument();
String text = doc.getText(0, doc.getLength());
int pos = 0;
if (!check.isSelected()) {
pattern = pattern.toLowerCase();
text = text.toLowerCase();
}
// Search for pattern
while ((pos = text.indexOf(pattern, pos)) >= 0) {
// Create highlighter using private painter and apply around
// pattern
hilite.addHighlight(pos, pos + pattern.length(), painter);
pos += pattern.length();
}
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
/**
* Appends \r\n to the end of your string, then it puts it on the top.
*
* @param t the string you want to append
*/
public void appendText(String t) {
textArea.setText((textArea.getText().isEmpty() ? "" : textArea
.getText() + "\r\n")
+ t);
textArea.setCaretPosition(0);
}
/**
* Sets the text
*
* @param t the text you want set
*/
public void setText(String t) {
textArea.setText(t);
textArea.setCaretPosition(0);
}
private static final long serialVersionUID = -6556940545421437508L;
}

View file

@ -29,7 +29,7 @@ import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath; import javax.swing.tree.TreePath;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer; import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.VisibleComponent; import the.bytecode.club.bytecodeviewer.gui.components.VisibleComponent;
import the.bytecode.club.bytecodeviewer.util.FileContainer; import the.bytecode.club.bytecodeviewer.util.FileContainer;
import the.bytecode.club.bytecodeviewer.util.FileDrop; import the.bytecode.club.bytecodeviewer.util.FileDrop;
import the.bytecode.club.bytecodeviewer.util.LazyNameUtil; import the.bytecode.club.bytecodeviewer.util.LazyNameUtil;

View file

@ -1,6 +1,6 @@
package the.bytecode.club.bytecodeviewer.gui.resourcelist; package the.bytecode.club.bytecodeviewer.gui.resourcelist;
import the.bytecode.club.bytecodeviewer.gui.extras.StringMetricsUtil; import the.bytecode.club.bytecodeviewer.gui.util.StringMetricsUtil;
import javax.swing.*; import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultMutableTreeNode;

View file

@ -0,0 +1,54 @@
package the.bytecode.club.bytecodeviewer.gui.resourcesearch;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI;
import the.bytecode.club.bytecodeviewer.searching.BackgroundSearchThread;
import the.bytecode.club.bytecodeviewer.searching.RegexInsnFinder;
import the.bytecode.club.bytecodeviewer.searching.RegexSearch;
import the.bytecode.club.bytecodeviewer.searching.SearchResultNotifier;
import the.bytecode.club.bytecodeviewer.util.FileContainer;
import javax.swing.tree.TreePath;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* @author Konloch
* @since 6/25/2021
*/
class PerformSearch extends BackgroundSearchThread
{
private final SearchBoxPane searchBoxPane;
private final SearchResultNotifier srn;
public PerformSearch(SearchBoxPane searchBoxPane, SearchResultNotifier srn)
{
this.searchBoxPane = searchBoxPane;
this.srn = srn;
}
@Override
public void doSearch()
{
try
{
Pattern.compile(RegexInsnFinder.processRegex(RegexSearch.searchText.getText()), Pattern.MULTILINE);
}
catch (PatternSyntaxException ex)
{
BytecodeViewer.showMessage("You have an error in your regex syntax.");
}
for (FileContainer container : BytecodeViewer.files)
for (ClassNode c : container.classes)
searchBoxPane.searchType.details.search(container, c, srn, searchBoxPane.exact.isSelected());
Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class)).search.setEnabled(true);
Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class)).search.setText("Search");
searchBoxPane.tree.expandPath(new TreePath(searchBoxPane.tree.getModel().getRoot()));
searchBoxPane.tree.updateUI();
}
}

View file

@ -1,11 +1,9 @@
package the.bytecode.club.bytecodeviewer.gui; package the.bytecode.club.bytecodeviewer.gui.resourcesearch;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.GridLayout; import java.awt.GridLayout;
import java.awt.event.ItemListener; import java.awt.event.ItemListener;
import java.util.Objects; import java.util.Objects;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JCheckBox; import javax.swing.JCheckBox;
@ -15,18 +13,15 @@ import javax.swing.JPanel;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JTree; import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer; import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane; import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.WorkPaneMainComponent;
import the.bytecode.club.bytecodeviewer.gui.components.VisibleComponent;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ResourceViewer;
import the.bytecode.club.bytecodeviewer.searching.BackgroundSearchThread; import the.bytecode.club.bytecodeviewer.searching.BackgroundSearchThread;
import the.bytecode.club.bytecodeviewer.searching.FieldCallSearch;
import the.bytecode.club.bytecodeviewer.searching.LDCSearch;
import the.bytecode.club.bytecodeviewer.searching.MethodCallSearch;
import the.bytecode.club.bytecodeviewer.searching.RegexInsnFinder;
import the.bytecode.club.bytecodeviewer.searching.RegexSearch;
import the.bytecode.club.bytecodeviewer.searching.SearchResultNotifier; import the.bytecode.club.bytecodeviewer.searching.SearchResultNotifier;
import the.bytecode.club.bytecodeviewer.searching.SearchTypeDetails;
import the.bytecode.club.bytecodeviewer.util.FileContainer; import the.bytecode.club.bytecodeviewer.util.FileContainer;
/*************************************************************************** /***************************************************************************
@ -55,8 +50,8 @@ import the.bytecode.club.bytecodeviewer.util.FileContainer;
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public class SearchBoxPane extends VisibleComponent { public class SearchBoxPane extends VisibleComponent
{
private static final long serialVersionUID = -1098524689236993932L; private static final long serialVersionUID = -1098524689236993932L;
public static final SearchRadius[] SEARCH_RADII = SearchRadius.values(); public static final SearchRadius[] SEARCH_RADII = SearchRadius.values();
public static final SearchType[] SEARCH_TYPES = SearchType.values(); public static final SearchType[] SEARCH_TYPES = SearchType.values();
@ -170,30 +165,7 @@ public class SearchBoxPane extends VisibleComponent {
final SearchResultNotifier srn = debug -> treeRoot.add(new DefaultMutableTreeNode(debug)); final SearchResultNotifier srn = debug -> treeRoot.add(new DefaultMutableTreeNode(debug));
if (radius == SearchRadius.All_Classes) { if (radius == SearchRadius.All_Classes) {
if (t.finished) { if (t.finished) {
t = new BackgroundSearchThread() { t = new PerformSearch(this, srn);
@Override
public void doSearch() {
try {
Pattern.compile(RegexInsnFinder.processRegex(RegexSearch.searchText.getText()), Pattern.MULTILINE);
} catch (PatternSyntaxException ex) {
BytecodeViewer.showMessage("You have an error in your regex syntax.");
}
for (FileContainer container : BytecodeViewer.files)
for (ClassNode c : container.classes)
searchType.details.search(container, c, srn, exact.isSelected());
Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class))
.search.setEnabled(true);
Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class))
.search.setText("Search");
tree.expandPath(new TreePath(tree.getModel().getRoot()));
tree.updateUI();
}
};
Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class)) Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class))
.search.setEnabled(false); .search.setEnabled(false);
Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class)) Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class))
@ -203,32 +175,16 @@ public class SearchBoxPane extends VisibleComponent {
BytecodeViewer.showMessage("You currently have a search performing in the background, please wait for that to finish."); BytecodeViewer.showMessage("You currently have a search performing in the background, please wait for that to finish.");
} }
} else if (radius == SearchRadius.Current_Class) { } else if (radius == SearchRadius.Current_Class) {
final Viewer cv = Objects.requireNonNull(MainViewerGUI.getComponent(WorkPane.class)).getCurrentViewer(); final ResourceViewer cv = Objects.requireNonNull(MainViewerGUI.getComponent(WorkPaneMainComponent.class)).getCurrentViewer();
if (cv != null) { if (cv != null) {
searchType.details.search(cv.container, cv.cn, srn, exact.isSelected()); searchType.details.search(cv.container, cv.cn, srn, exact.isSelected());
} }
} }
} }
public enum SearchType {
Strings(new LDCSearch()),
Regex(new RegexSearch()),
MethodCall(new MethodCallSearch()),
FieldCall(new FieldCallSearch());
public final SearchTypeDetails details;
SearchType(final SearchTypeDetails details) {
this.details = details;
}
}
public enum SearchRadius {
All_Classes, Current_Class
}
public void resetWorkspace() { public void resetWorkspace() {
treeRoot.removeAllChildren(); treeRoot.removeAllChildren();
tree.updateUI(); tree.updateUI();
} }
} }

View file

@ -0,0 +1,10 @@
package the.bytecode.club.bytecodeviewer.gui.resourcesearch;
/**
* @author Konloch
* @since 6/25/2021
*/
public enum SearchRadius
{
All_Classes, Current_Class
}

View file

@ -0,0 +1,22 @@
package the.bytecode.club.bytecodeviewer.gui.resourcesearch;
import the.bytecode.club.bytecodeviewer.searching.*;
/**
* @author Konloch
* @since 6/25/2021
*/
public enum SearchType
{
Strings(new LDCSearch()),
Regex(new RegexSearch()),
MethodCall(new MethodCallSearch()),
FieldCall(new FieldCallSearch());
public final SearchTypeDetails details;
SearchType(final SearchTypeDetails details)
{
this.details = details;
}
}

View file

@ -1,4 +1,7 @@
package the.bytecode.club.bytecodeviewer.gui; package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import the.bytecode.club.bytecodeviewer.gui.components.DecompilerViewComponent;
import javax.swing.*; import javax.swing.*;
@ -6,7 +9,7 @@ import javax.swing.*;
* @author Konloch * @author Konloch
* @since 6/21/2021 * @since 6/21/2021
*/ */
public class ViewPane public class DecompilerSelectionPane
{ {
public final int paneID; public final int paneID;
public final JMenu menu; public final JMenu menu;
@ -23,7 +26,7 @@ public class ViewPane
public final JRadioButtonMenuItem bytecode = new JRadioButtonMenuItem("Bytecode"); public final JRadioButtonMenuItem bytecode = new JRadioButtonMenuItem("Bytecode");
public final JRadioButtonMenuItem asmTextify = new JRadioButtonMenuItem("ASM Textify"); public final JRadioButtonMenuItem asmTextify = new JRadioButtonMenuItem("ASM Textify");
public ViewPane(int paneID) { public DecompilerSelectionPane(int paneID) {
this.paneID = paneID; this.paneID = paneID;
this.menu = new JMenu("Pane " + paneID); this.menu = new JMenu("Pane " + paneID);
buildMenu(); buildMenu();

View file

@ -0,0 +1,12 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
/**
* @author Konloch
* @since 6/25/2021
*/
public enum ResourcePanelCompileMode
{
JAVA,
KRAKATAU_ASSEMBLY,
SMALI_ASSEMBLY,
}

View file

@ -0,0 +1,107 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.objectweb.asm.ClassWriter;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import the.bytecode.club.bytecodeviewer.gui.components.SearchableRSyntaxTextArea;
import the.bytecode.club.bytecodeviewer.gui.hexviewer.JHexEditor;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.gui.util.PaneUpdaterThread;
import javax.swing.*;
import java.awt.*;
/**
* @author Konloch
* @since 6/24/2021
*/
public class ResourceViewPanel
{
public final JPanel panel = new JPanel(new BorderLayout());
public ClassViewer viewer;
public int decompilerViewIndex = -1;
public SearchableRSyntaxTextArea textArea;
public PaneUpdaterThread updateThread;
public final int panelIndex;
//TODO change the compile mode for Krakatau and Smali assembly
public ResourcePanelCompileMode compileMode = ResourcePanelCompileMode.JAVA;
public ResourceViewPanel(int panelIndex) {this.panelIndex = panelIndex;}
public void createPane(ClassViewer viewer)
{
panel.removeAll();
textArea = null;
if(viewer.cn == null)
{
panel.add(new JLabel("This file has been removed from the reload."));
}
}
public void updatePane(ClassViewer cv, byte[] b, JButton button, boolean isPanelEditable)
{
updateThread = new PaneUpdaterThread(panelIndex, decompilerViewIndex) {
@Override
public void doShit() {
try {
BytecodeViewer.viewer.updateBusyStatus(true);
if(ResourceViewPanel.this.decompilerViewIndex > 0)
{
//hex viewer
if (ResourceViewPanel.this.decompilerViewIndex == 5)
{
final ClassWriter cw = new ClassWriter(0);
cv.cn.accept(cw);
final JHexEditor hex = new JHexEditor(cw.toByteArray());
hex.setFont(new Font(Font.MONOSPACED, Font.PLAIN, (int) BytecodeViewer.viewer.fontSpinner.getValue()));
SwingUtilities.invokeLater(() -> panel.add(hex));
}
else
{
viewer = cv;
updateUpdaterTextArea = (SearchableRSyntaxTextArea) Configuration.rstaTheme.apply(new SearchableRSyntaxTextArea());
final Decompiler decompiler = Decompiler.decompilersByIndex.get(ResourceViewPanel.this.decompilerViewIndex);
final String decompiledSource = decompiler.getDecompiler().decompileClassNode(cv.cn, b);
//decompilerUpdate.update(decompiler.getDecompilerName(), cv.cn, b, decompiler.getDecompiler(), updateUpdaterTextArea);
SwingUtilities.invokeLater(() ->
{
panel.add(updateUpdaterTextArea.getScrollPane());
textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
textArea.setCodeFoldingEnabled(true);
textArea.setAntiAliasingEnabled(true);
textArea.setText(decompiledSource);
textArea.setCaretPosition(0);
textArea.setEditable(isPanelEditable);
textArea.getScrollPane().setColumnHeaderView(new JLabel(decompiler.getDecompilerName() + " - Editable: " + textArea.isEditable()));
textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN,
(int) BytecodeViewer.viewer.fontSpinner.getValue()));
});
textArea = updateUpdaterTextArea;
}
}
} catch (java.lang.IndexOutOfBoundsException | java.lang.NullPointerException e) {
//ignore
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
} finally {
cv.resetDivider();
BytecodeViewer.viewer.updateBusyStatus(false);
if (button != null)
button.setEnabled(true);
}
}
};
}
}

View file

@ -0,0 +1,100 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import javax.swing.*;
import javax.swing.plaf.basic.BasicButtonUI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* @author Konloch
* @since 6/25/2021
*/
public class TabButton extends JButton implements ActionListener
{
private final TabbedPane tabbedPane;
private final int tabIndex;
private final String tabWorkingName;
public TabButton(TabbedPane tabbedPane, int tabIndex, String tabWorkingName)
{
this.tabbedPane = tabbedPane;
this.tabIndex = tabIndex;
this.tabWorkingName = tabWorkingName;
final int size = 17;
setPreferredSize(new Dimension(size, size));
setToolTipText("Close this tab");
// Make the button looks the same for all Laf's
setUI(new BasicButtonUI());
// Make it transparent
setContentAreaFilled(false);
// No need to be focusable
setFocusable(false);
setBorder(BorderFactory.createEtchedBorder());
setBorderPainted(false);
// Making nice rollover effect
// we use the same listener for all buttons
addMouseListener(TabbedPane.buttonHoverAnimation);
setRolloverEnabled(true);
// Close the proper tab by clicking the button
addActionListener(this);
}
public int getTabIndex()
{
return tabIndex;
}
@Override
public void actionPerformed(final ActionEvent e)
{
final int i = tabbedPane.tabs.indexOfTabComponent(tabbedPane);
if (i != -1)
{
tabbedPane.tabs.remove(i);
}
}
// we don't want to update UI for this button
@Override
public void updateUI() { }
// paint the cross
@Override
protected void paintComponent(final Graphics g)
{
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g.create();
// shift the image for pressed buttons
if (getModel().isPressed())
g2.translate(1, 1);
g2.setStroke(new BasicStroke(2));
g2.setColor(Color.BLACK);
if (getModel().isRollover())
g2.setColor(Color.MAGENTA);
final int delta = 6;
g2.drawLine(delta, delta, getWidth() - delta - 1, getHeight() - delta - 1);
g2.drawLine(getWidth() - delta - 1, delta, delta, getHeight() - delta - 1);
g2.dispose();
}
public TabbedPane getTabbedPane()
{
return tabbedPane;
}
public String getTabWorkingName()
{
return tabWorkingName;
}
public static long getSerialVersionUID()
{
return serialVersionUID;
}
private static final long serialVersionUID = -4492967978286454159L;
}

View file

@ -0,0 +1,36 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.FileViewer;
import java.awt.*;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
/**
* @author Konloch
* @since 6/24/2021
*/
public class TabRemovalEvent implements ContainerListener
{
@Override
public void componentAdded(ContainerEvent e) { }
@Override
public void componentRemoved(ContainerEvent e)
{
final Component c = e.getChild();
if (c instanceof ClassViewer)
{
String workingName = ((ClassViewer) c).workingName;
BytecodeViewer.viewer.workPane.workingOn.remove(workingName);
}
else if (c instanceof FileViewer)
{
String workingName = ((FileViewer) c).workingName;
BytecodeViewer.viewer.workPane.workingOn.remove(workingName);
}
}
}

View file

@ -0,0 +1,250 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.*;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.components.ButtonHoverAnimation;
import the.bytecode.club.bytecodeviewer.gui.components.MaxWidthJLabel;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.MouseClickedListener;
import the.bytecode.club.bytecodeviewer.gui.util.DelayTabbedPaneThread;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* Component to be used as tabComponent; Contains a JLabel to show the text and a JButton to close the tab it belongs to
*
* @author Konloch
* @author WaterWolf
*/
public class TabbedPane extends JPanel
{
public final JTabbedPane tabs;
public final JLabel label;
private DelayTabbedPaneThread probablyABadIdea;
private long startedDragging = 0;
public final String tabName;
public final String fileContainerName;
private static long lastMouseClick = System.currentTimeMillis();
public final static MouseListener buttonHoverAnimation = new ButtonHoverAnimation();
public static final Color BLANK_COLOR = new Color(0, 0, 0, 0);
public TabbedPane(int tabIndex, String tabWorkingName, String fileContainerName, String name, final JTabbedPane existingTabs)
{
// unset default FlowLayout' gaps
super(new FlowLayout(FlowLayout.LEFT, 0, 0));
this.tabName = name;
this.fileContainerName = fileContainerName;
if (existingTabs == null)
throw new NullPointerException("TabbedPane is null");
this.tabs = existingTabs;
setOpaque(false);
// make JLabel read titles from JTabbedPane
label = new MaxWidthJLabel(tabName, 400, 20);
this.add(label);
// add more space between the label and the button
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
// tab button
JButton button = new TabButton(this, tabIndex, tabWorkingName);
this.add(button);
// add more space to the top of the component
setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
//define the right click pop-up menu
JPopupMenu rightClickMenu = new JPopupMenu();
JMenuItem closeAllTabs = new JMenuItem("Close All But This: " + name);
JMenuItem closeTab = new JMenuItem("Close Tab: " + name);
rightClickMenu.add(closeAllTabs);
rightClickMenu.add(closeTab);
//setComponentPopupMenu(rightClickMenu);
button.setComponentPopupMenu(rightClickMenu);
button.addMouseListener(new MouseClickedListener(e ->
{
if (e.getModifiers() != InputEvent.ALT_MASK || System.currentTimeMillis() - lastMouseClick < 100)
return;
lastMouseClick = System.currentTimeMillis();
final int i = existingTabs.indexOfTabComponent(TabbedPane.this);
if (i != -1)
existingTabs.remove(i);
}));
closeTab.addActionListener(e ->
{
TabButton tabButton = (TabButton) ((JPopupMenu)((JMenuItem) e.getSource()).getParent()).getInvoker();
final int index = tabButton.getTabIndex();
if (index != -1)
existingTabs.remove(index);
});
closeAllTabs.addActionListener(e ->
{
TabButton tabButton = (TabButton) ((JPopupMenu)((JMenuItem) e.getSource()).getParent()).getInvoker();
final int index = tabButton.getTabIndex();
while (true)
{
if (existingTabs.getTabCount() <= 1)
return;
if (index != 0)
existingTabs.remove(0);
else
existingTabs.remove(1);
}
});
//tab dragging
if(BytecodeViewer.EXPERIMENTAL_TAB_CODE)
{
/*label.addMouseListener(new MouseListener() {
@Override public void mouseClicked(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent arg0) {
}
@Override public void mouseExited(MouseEvent arg0) {
}
@Override public void mousePressed(MouseEvent e) {
onMousePressed(e);
}
@Override public void mouseReleased(MouseEvent e) {
stopDragging(e.getX(), e.getY());
}
});*/
this.addMouseListener(new MouseListener() {
@Override public void mouseClicked(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent arg0) {}
@Override public void mouseExited(MouseEvent arg0) {}
@Override public void mousePressed(MouseEvent e) {
onMousePressed(e);
}
@Override public void mouseReleased(MouseEvent e) {
stopDragging(e.getX(), e.getY());
}
});
}
//middle click close
if(BytecodeViewer.EXPERIMENTAL_TAB_CODE)
{
this.addMouseListener(new MouseAdapter()
{
@Override
public void mouseReleased(MouseEvent e)
{
if (e.getButton() != MouseEvent.BUTTON2)
return;
final int i = existingTabs.indexOfTabComponent(TabbedPane.this);
if (i != -1)
existingTabs.remove(i);
}
});
}
}
private void stopDragging(int mouseX, int mouseY)
{
if (System.currentTimeMillis() - startedDragging >= 210)
{
Rectangle bounds = new Rectangle(1, 1, mouseX, mouseY);
System.out.println("debug-5: " + mouseX + ", " + mouseY);
int totalTabs = BytecodeViewer.viewer.workPane.tabs.getTabCount();
int index = -1;
for (int i = 0; i < totalTabs; i++)
{
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if (c != null && bounds.intersects(c.getBounds()))
index = i; //replace this tabs position
}
if (index == -1)
{
for (int i = 0; i < totalTabs; i++)
{
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
//do some check to see if it's past the X or Y
if (c != null) {
System.out.println("debug-6: " + c.getBounds());
}
}
}
if (index != -1)
{
BytecodeViewer.viewer.workPane.tabs.remove(this);
BytecodeViewer.viewer.workPane.tabs.setTabComponentAt(index, this);
}
}
SwingUtilities.invokeLater(() ->
{
label.setBackground(BLANK_COLOR);
label.updateUI();
});
}
public void onMousePressed(MouseEvent e)
{
BytecodeViewer.viewer.workPane.tabs.dispatchEvent(e);
if(e.getButton() == 1)
{
startedDragging = System.currentTimeMillis();
//dragging = true;
if (probablyABadIdea != null)
probablyABadIdea.stopped = true;
probablyABadIdea = new DelayTabbedPaneThread(TabbedPane.this);
probablyABadIdea.start();
repaint();
System.out.println(e.getX()+", "+e.getY());
Rectangle bounds = new Rectangle(1, 1, e.getX(), e.getY());
for(int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++)
{
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if(c != null && bounds.intersects(c.getBounds()))
BytecodeViewer.viewer.workPane.tabs.setSelectedIndex(i);
}
}
else
{
stopDragging(e.getX(), e.getY());
}
}
private static final long serialVersionUID = -4774885688297538774L;
}

View file

@ -0,0 +1,235 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashMap;
import javax.swing.JButton;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.gui.components.VisibleComponent;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.FileViewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ResourceViewer;
import the.bytecode.club.bytecodeviewer.util.FileContainer;
import the.bytecode.club.bytecodeviewer.util.MiscUtils;
import static the.bytecode.club.bytecodeviewer.Constants.BLOCK_TAB_MENU;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* The pane that contains all of the resources as tabs.
*
* @author Konloch
* @author WaterWolf
*/
public class WorkPaneMainComponent extends VisibleComponent
{
public final JTabbedPane tabs;
public final JPanel buttonPanel;
public final JButton refreshClass;
public final HashMap<String, Integer> workingOn = new HashMap<>();
public WorkPaneMainComponent()
{
super("WorkPanel");
setTitle("Work Space");
this.tabs = new JTabbedPane();
JPopupMenu popUp = new JPopupMenu();
JMenuItem closeAllTabs = new JMenuItem("Close All But This");
JMenuItem closeTab = new JMenuItem("Close Tab");
closeTab.addActionListener(e ->
{
TabButton tabButton = (TabButton) ((JPopupMenu)((JMenuItem) e.getSource()).getParent()).getInvoker();
final int index = tabButton.getTabIndex();
if (index != -1)
tabs.remove(index);
});
closeAllTabs.addActionListener(e ->
{
TabButton tabButton = (TabButton) ((JPopupMenu)((JMenuItem) e.getSource()).getParent()).getInvoker();
final int index = tabButton.getTabIndex();
while (true)
{
if (tabs.getTabCount() <= 1)
return;
if (index != 0)
tabs.remove(0);
else
tabs.remove(1);
}
});
tabs.addMouseListener(new MouseListener()
{
@Override
public void mouseClicked(MouseEvent e) { }
@Override
public void mouseEntered(MouseEvent arg0) { }
@Override
public void mouseExited(MouseEvent arg0) { }
@Override
public void mousePressed(MouseEvent e)
{
if (BLOCK_TAB_MENU)
return;
if (e.getButton() == 3)
{
Rectangle bounds = new Rectangle(1, 1, e.getX(), e.getY());
for (int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++)
{
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if (c != null && bounds.intersects(c.getBounds()))
{
popUp.setVisible(true);
closeAllTabs.setText("Close All But This: " + ((TabbedPane) c).tabName);
closeTab.setText("Close Tab: " + ((TabbedPane) c).tabName);
}
else
{
popUp.setVisible(false);
}
}
}
}
@Override
public void mouseReleased(MouseEvent e) { }
});
popUp.add(closeAllTabs);
popUp.add(closeTab);
if (!BLOCK_TAB_MENU)
tabs.setComponentPopupMenu(popUp);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(tabs, BorderLayout.CENTER);
buttonPanel = new JPanel(new FlowLayout());
refreshClass = new JButton("Refresh");
refreshClass.addActionListener((event)->{
Thread t = new Thread(() -> new WorkPaneRefresh(event).run());
t.start();
});
buttonPanel.add(refreshClass);
buttonPanel.setVisible(false);
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
tabs.addContainerListener(new TabRemovalEvent());
tabs.addChangeListener(arg0 -> buttonPanel.setVisible(tabs.getSelectedIndex() != -1));
this.setVisible(true);
}
public void addWorkingFile(final FileContainer container, String name, final ClassNode cn)
{
String workingName = container.name + ">" + name;
String containerName = name;
if (Configuration.displayParentInTab)
containerName = container.name + ">" + name;
if (!workingOn.containsKey(workingName))
{
final ClassViewer tabComp = new ClassViewer(container, containerName, cn, workingName);
tabs.add(tabComp);
final int tabIndex = tabs.indexOfComponent(tabComp);
workingOn.put(workingName, tabIndex);
TabbedPane tabbedPane = new TabbedPane(tabIndex, workingName, container.name, name, tabs);
tabComp.tabbedPane = tabbedPane;
tabs.setTabComponentAt(tabIndex, tabbedPane);
tabs.setSelectedIndex(tabIndex);
}
else
{
tabs.setSelectedIndex(workingOn.get(workingName));
}
}
public void addFile(final FileContainer container, String name, byte[] contents)
{
if (contents == null) //a directory
return;
final String workingName = container.name + ">" + name;
if (Configuration.simplifiedTabNames)
name = MiscUtils.getChildFromPath(name);
if (Configuration.displayParentInTab)
name = container.name + ">" + name;
if (!workingOn.containsKey(workingName))
{
final FileViewer tabComp = new FileViewer(container, name, contents, workingName);
tabs.add(tabComp);
final int tabIndex = tabs.indexOfComponent(tabComp);
workingOn.put(workingName, tabIndex);
TabbedPane tabbedPane = new TabbedPane(tabIndex, workingName, container.name, name, tabs);
tabComp.tabbedPane = tabbedPane;
tabs.setTabComponentAt(tabIndex, tabbedPane);
tabs.setSelectedIndex(tabIndex);
}
else
{
try {
tabs.setSelectedIndex(workingOn.get(workingName));
} catch (Exception e) {
//workingOn.remove(workingName);
e.printStackTrace();
}
}
}
public ResourceViewer getCurrentViewer() {
return (ResourceViewer) tabs.getSelectedComponent();
}
public Component[] getLoadedViewers() {
return tabs.getComponents();
}
public void resetWorkspace()
{
tabs.removeAll();
tabs.updateUI();
}
private static final long serialVersionUID = 6542337997679487946L;
}

View file

@ -0,0 +1,65 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.FileViewer;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
/**
* @author Konloch
* @since 6/24/2021
*/
public class WorkPaneRefresh implements Runnable
{
private final ActionEvent event;
public WorkPaneRefresh(ActionEvent event) {
this.event = event;
}
@Override
public void run()
{
if (BytecodeViewer.viewer.autoCompileOnRefresh.isSelected())
try {
if (!BytecodeViewer.compile(false))
return;
} catch (NullPointerException ignored) {
}
JButton src = null;
if(event != null && event.getSource() instanceof JButton)
src = (JButton) event.getSource();
//if (src == BytecodeViewer.viewer.workPane.refreshClass)
{
final Component tabComp = BytecodeViewer.viewer.workPane.tabs.getSelectedComponent();
if(tabComp == null)
return;
if (tabComp instanceof ClassViewer)
{
if(src != null)
src.setEnabled(false);
BytecodeViewer.viewer.updateBusyStatus(true);
((ClassViewer) tabComp).startPaneUpdater(src);
BytecodeViewer.viewer.updateBusyStatus(false);
}
else if (tabComp instanceof FileViewer)
{
if(src != null)
src.setEnabled(false);
BytecodeViewer.viewer.updateBusyStatus(true);
((FileViewer) tabComp).refresh(src);
BytecodeViewer.viewer.updateBusyStatus(false);
}
}
}
}

View file

@ -0,0 +1,569 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.ResourcePanelCompileMode;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.ResourceViewPanel;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.TabbedPane;
import the.bytecode.club.bytecodeviewer.gui.hexviewer.JHexEditor;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JSplitPane;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.Settings;
import the.bytecode.club.bytecodeviewer.util.FileContainer;
import the.bytecode.club.bytecodeviewer.util.MethodParser;
import static the.bytecode.club.bytecodeviewer.util.MethodParser.Method;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* This represents the opened classfile.
*
* @author Konloch
* @author WaterWolf
*/
public class ClassViewer extends ResourceViewer
{
private static final long serialVersionUID = -8650495368920680024L;
public String name;
public JSplitPane sp;
public JSplitPane sp2;
public TabbedPane tabbedPane;
public ResourceViewPanel resourceViewPanel1 = new ResourceViewPanel(0);
public ResourceViewPanel resourceViewPanel2 = new ResourceViewPanel(1);
public ResourceViewPanel resourceViewPanel3 = new ResourceViewPanel(2);
public File[] tempFiles;
public ClassViewer THIS = this;
public List<MethodParser> methods = Arrays.asList(new MethodParser(), new MethodParser(), new MethodParser());
public final String workingName;
private final DefaultHighlighter.DefaultHighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(new Color(255, 62, 150));
/**
* This was really interesting to write.
*
* @author Konloch
*/
public void search(int pane, String search, boolean next)
{
try
{
Component[] com = null;
if (pane == 0) // bytecode
com = resourceViewPanel1.panel.getComponents();
else if (pane == 1)
com = resourceViewPanel2.panel.getComponents();
else if (pane == 2)
com = resourceViewPanel3.panel.getComponents();
if (com == null) // someone fucked up, lets prevent a nullpointer.
return;
for (Component c : com)
{
if (c instanceof RTextScrollPane)
{
RSyntaxTextArea area = (RSyntaxTextArea) ((RTextScrollPane) c).getViewport().getComponent(0);
if (search.isEmpty())
{
highlight(pane, area, "");
return;
}
int startLine = area.getDocument().getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1;
int currentLine = 1;
boolean canSearch = false;
String[] test = (area.getText().split("\n").length >= 2
? area.getText().split("\n")
: area.getText().split("\r"));
int lastGoodLine = -1;
int firstPos = -1;
boolean found = false;
if (next)
{
for (String s : test)
{
if (pane == 0 && !resourceViewPanel1.textArea.getCaseSensitiveSearch().isSelected() ||
pane == 1 && !resourceViewPanel2.textArea.getCaseSensitiveSearch().isSelected())
{
//TODO should pane2 be here? - look into this later
s = s.toLowerCase();
search = search.toLowerCase();
}
if (currentLine == startLine)
{
canSearch = true;
}
else if (s.contains(search))
{
if (canSearch)
{
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(currentLine - 1)
.getStartOffset());
canSearch = false;
found = true;
}
if (firstPos == -1)
firstPos = currentLine;
}
currentLine++;
}
if (!found && firstPos != -1)
{
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(firstPos - 1).getStartOffset());
}
}
else
{
canSearch = true;
for (String s : test)
{
if (pane == 0 && !resourceViewPanel1.textArea.getCaseSensitiveSearch().isSelected() || pane == 1
&& !resourceViewPanel2.textArea.getCaseSensitiveSearch().isSelected() || pane == 2
&& !resourceViewPanel3.textArea.getCaseSensitiveSearch().isSelected())
{
s = s.toLowerCase();
search = search.toLowerCase();
}
if (s.contains(search))
{
if (lastGoodLine != -1 && canSearch)
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1)
.getStartOffset());
lastGoodLine = currentLine;
if (currentLine >= startLine)
canSearch = false;
}
currentLine++;
}
if (lastGoodLine != -1
&& area.getDocument()
.getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1 == startLine)
{
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1)
.getStartOffset());
}
}
highlight(pane, area, search);
}
}
}
catch (Exception e)
{
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
public void highlight(int pane, JTextComponent textComp, String pattern)
{
if (pattern.isEmpty())
{
textComp.getHighlighter().removeAllHighlights();
return;
}
try
{
Highlighter hilite = textComp.getHighlighter();
hilite.removeAllHighlights();
javax.swing.text.Document doc = textComp.getDocument();
String text = doc.getText(0, doc.getLength());
int pos = 0;
if ((pane == 0 && !resourceViewPanel1.textArea.getCaseSensitiveSearch().isSelected()) || pane == 1
&& !resourceViewPanel2.textArea.getCaseSensitiveSearch().isSelected() || pane == 2
&& !resourceViewPanel3.textArea.getCaseSensitiveSearch().isSelected())
{
pattern = pattern.toLowerCase();
text = text.toLowerCase();
}
// Search for pattern
while ((pos = text.indexOf(pattern, pos)) >= 0)
{
// Create highlighter using private painter and apply around
// pattern
hilite.addHighlight(pos, pos + pattern.length(), painter);
pos += pattern.length();
}
}
catch (Exception e)
{
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
public ClassViewer(final FileContainer container, final String name, final ClassNode cn, String workingName)
{
this.workingName = workingName;
this.container = container;
this.name = name;
this.cn = cn;
this.setName(name);
this.setLayout(new BorderLayout());
this.sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, resourceViewPanel1.panel, resourceViewPanel2.panel);
final ClassWriter cw = new ClassWriter(0);
cn.accept(cw);
JHexEditor hex = new JHexEditor(cw.toByteArray());
this.sp2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, sp, resourceViewPanel3.panel);
this.add(sp2, BorderLayout.CENTER);
hex.setMaximumSize(new Dimension(0, Integer.MAX_VALUE));
hex.setSize(0, Integer.MAX_VALUE);
startPaneUpdater(null);
this.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
resetDivider();
}
});
}
public void resetDivider() {
SwingUtilities.invokeLater(() ->
{
sp.setResizeWeight(0.5);
if (resourceViewPanel2.decompilerViewIndex != 0 && resourceViewPanel1.decompilerViewIndex != 0) {
setDividerLocation(sp, 0.5);
} else if (resourceViewPanel1.decompilerViewIndex != 0) {
setDividerLocation(sp, 1);
} else if (resourceViewPanel2.decompilerViewIndex != 0) {
sp.setResizeWeight(1);
setDividerLocation(sp, 0);
} else {
setDividerLocation(sp, 0);
}
if (resourceViewPanel3.decompilerViewIndex != 0) {
sp2.setResizeWeight(0.7);
setDividerLocation(sp2, 0.7);
if ((resourceViewPanel2.decompilerViewIndex == 0 && resourceViewPanel1.decompilerViewIndex != 0)
|| (resourceViewPanel1.decompilerViewIndex == 0 && resourceViewPanel2.decompilerViewIndex != 0)) {
setDividerLocation(sp2, 0.5);
} else if (resourceViewPanel1.decompilerViewIndex == 0) {
setDividerLocation(sp2, 0);
}
} else {
sp.setResizeWeight(1);
sp2.setResizeWeight(0);
setDividerLocation(sp2, 1);
}
});
}
public void startPaneUpdater(final JButton button)
{
this.cn = BytecodeViewer.getClassNode(container, cn.name); //update the classnode
setPanes();
resourceViewPanel1.createPane(this);
resourceViewPanel2.createPane(this);
resourceViewPanel3.createPane(this);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch (Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException ignored) { }
}
final byte[] b = cw.toByteArray();
resourceViewPanel1.updatePane(this, b, button, isPanel1Editable());
resourceViewPanel2.updatePane(this, b, button, isPanel2Editable());
resourceViewPanel3.updatePane(this, b, button, isPanel3Editable());
Thread t = new Thread(() ->
{
BytecodeViewer.viewer.updateBusyStatus(true);
while (Configuration.currentlyDumping)
{
//wait until it's not dumping
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
tempFiles = BytecodeViewer.dumpTempFile(container);
BytecodeViewer.viewer.updateBusyStatus(false);
if (resourceViewPanel1.decompilerViewIndex > 0)
resourceViewPanel1.updateThread.startNewThread();
if (resourceViewPanel2.decompilerViewIndex > 0)
resourceViewPanel2.updateThread.startNewThread();
if (resourceViewPanel3.decompilerViewIndex > 0)
resourceViewPanel3.updateThread.startNewThread();
});
t.start();
if (isPanel1Editable() || isPanel2Editable() || isPanel3Editable())
{
if (Configuration.warnForEditing)
return;
Configuration.warnForEditing = true;
if (!BytecodeViewer.viewer.autoCompileOnRefresh.isSelected()
&& !BytecodeViewer.viewer.compileOnSave.isSelected())
{
BytecodeViewer.showMessage("Make sure to compile (File>Compile or Ctrl + T) whenever you want to "
+ "test or export your changes.\nYou can set compile automatically on refresh or on save "
+ "in the settings menu.");
Settings.saveSettings();
}
}
}
public Object[] getSmali() {
if (resourceViewPanel1.compileMode == ResourcePanelCompileMode.SMALI_ASSEMBLY)
return new Object[]{cn, resourceViewPanel1.textArea.getText()};
if (resourceViewPanel2.compileMode == ResourcePanelCompileMode.SMALI_ASSEMBLY)
return new Object[]{cn, resourceViewPanel2.textArea.getText()};
if (resourceViewPanel3.compileMode == ResourcePanelCompileMode.SMALI_ASSEMBLY)
return new Object[]{cn, resourceViewPanel3.textArea.getText()};
return null;
}
public Object[] getKrakatau() {
if (resourceViewPanel1.compileMode == ResourcePanelCompileMode.KRAKATAU_ASSEMBLY)
return new Object[]{cn, resourceViewPanel1.textArea.getText()};
if (resourceViewPanel2.compileMode == ResourcePanelCompileMode.KRAKATAU_ASSEMBLY)
return new Object[]{cn, resourceViewPanel2.textArea.getText()};
if (resourceViewPanel3.compileMode == ResourcePanelCompileMode.KRAKATAU_ASSEMBLY)
return new Object[]{cn, resourceViewPanel3.textArea.getText()};
return null;
}
public Object[] getJava() {
if (resourceViewPanel1.textArea != null)
return new Object[]{cn, resourceViewPanel1.textArea.getText()};
if (resourceViewPanel2.textArea != null)
return new Object[]{cn, resourceViewPanel2.textArea.getText()};
if (resourceViewPanel3.textArea != null)
return new Object[]{cn, resourceViewPanel3.textArea.getText()};
return null;
}
public void setPanes() {
resourceViewPanel1.decompilerViewIndex = BytecodeViewer.viewer.viewPane1.getSelectedViewer();
resourceViewPanel2.decompilerViewIndex = BytecodeViewer.viewer.viewPane2.getSelectedViewer();
resourceViewPanel3.decompilerViewIndex = BytecodeViewer.viewer.viewPane3.getSelectedViewer();
}
public boolean isPanel1Editable() {
setPanes();
return BytecodeViewer.viewer.viewPane1.isPaneEditable();
}
public boolean isPanel2Editable() {
setPanes();
return BytecodeViewer.viewer.viewPane2.isPaneEditable();
}
public boolean isPanel3Editable() {
setPanes();
return BytecodeViewer.viewer.viewPane3.isPaneEditable();
}
/**
* Whoever wrote this function, THANK YOU!
*
* @param splitter
* @param proportion
* @return
*/
public static JSplitPane setDividerLocation(final JSplitPane splitter,
final double proportion) {
if (splitter.isShowing()) {
if (splitter.getWidth() > 0 && splitter.getHeight() > 0) {
splitter.setDividerLocation(proportion);
} else {
splitter.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent ce) {
splitter.removeComponentListener(this);
setDividerLocation(splitter, proportion);
}
});
}
} else {
splitter.addHierarchyListener(new HierarchyListener() {
@Override
public void hierarchyChanged(HierarchyEvent e) {
if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0
&& splitter.isShowing()) {
splitter.removeHierarchyListener(this);
setDividerLocation(splitter, proportion);
}
}
});
}
return splitter;
}
public static void selectMethod(RSyntaxTextArea area, int methodLine) {
if (methodLine != area.getCaretLineNumber()) {
setCaretLine(area, methodLine);
setViewLine(area, methodLine);
}
}
public static void selectMethod(ClassViewer classViewer, int paneId, Method method) {
RSyntaxTextArea area = null;
switch (paneId) {
case 0:
area = classViewer.resourceViewPanel1.updateThread.updateUpdaterTextArea;
break;
case 1:
area = classViewer.resourceViewPanel2.updateThread.updateUpdaterTextArea;
break;
case 2:
area = classViewer.resourceViewPanel3.updateThread.updateUpdaterTextArea;
break;
}
if (area != null) {
MethodParser methods = classViewer.methods.get(paneId);
if (methods != null) {
int methodLine = methods.findMethod(method);
if (methodLine != -1) {
selectMethod(area, methodLine);
}
}
}
}
public static String getLineText(RSyntaxTextArea area, int line) {
try {
if (line < area.getLineCount()) {
int start = area.getLineStartOffset(line);
int end = area.getLineEndOffset(line);
return area.getText(start, end - start).trim();
}
} catch (BadLocationException ignored) { }
return "";
}
public static int getMaxViewLine(RSyntaxTextArea area)
{
Container parent = area.getParent();
if (parent instanceof JViewport)
{
JViewport viewport = (JViewport) parent;
int y = viewport.getViewSize().height - viewport.getExtentSize().height;
int lineHeight = area.getLineHeight();
return y >= lineHeight ? y / lineHeight : 0;
}
return 0;
}
public static int getViewLine(RSyntaxTextArea area)
{
Container parent = area.getParent();
if (parent instanceof JViewport)
{
JViewport viewport = (JViewport) parent;
Point point = viewport.getViewPosition();
int lineHeight = area.getLineHeight();
return point.y >= lineHeight ? point.y / lineHeight : 0;
}
return 0;
}
public static void setViewLine(RSyntaxTextArea area, int line)
{
Container parent = area.getParent();
if (parent instanceof JViewport)
{
JViewport viewport = (JViewport) parent;
int maxLine = ClassViewer.getMaxViewLine(area);
line = Math.min(line, maxLine);
viewport.setViewPosition(new Point(0, line * area.getLineHeight()));
}
}
public static void setCaretLine(RSyntaxTextArea area, int line)
{
try {
area.setCaretPosition(area.getLineStartOffset(line));
} catch (BadLocationException ignored) { }
}
}

View file

@ -0,0 +1,149 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer;
import java.awt.BorderLayout;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.imgscalr.Scalr;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.Resources;
import the.bytecode.club.bytecodeviewer.gui.components.ImageJLabel;
import the.bytecode.club.bytecodeviewer.gui.components.SearchableRSyntaxTextArea;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.PressKeyListener;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.ReleaseKeyListener;
import the.bytecode.club.bytecodeviewer.gui.hexviewer.JHexEditor;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.TabbedPane;
import the.bytecode.club.bytecodeviewer.util.FileContainer;
import the.bytecode.club.bytecodeviewer.util.MiscUtils;
import the.bytecode.club.bytecodeviewer.util.SyntaxLanguage;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* Represents any open non-class file.
*
* @author Konloch
*/
public class FileViewer extends ResourceViewer
{
public final String name;
public final byte[] contents;
public final String workingName;
public final SearchableRSyntaxTextArea textArea = (SearchableRSyntaxTextArea)
Configuration.rstaTheme.apply(new SearchableRSyntaxTextArea());
public final JPanel mainPanel = new JPanel(new BorderLayout());
public TabbedPane tabbedPane;
public BufferedImage image;
public boolean canRefresh;
public FileViewer(final FileContainer container, final String name, final byte[] contents, String workingName)
{
this.name = name;
this.contents = contents;
this.workingName = workingName;
this.container = container;
this.setName(name);
this.setLayout(new BorderLayout());
this.add(mainPanel, BorderLayout.CENTER);
setContents();
}
public void setContents()
{
final String nameLowerCase = this.name.toLowerCase();
final String contentsAsString = new String(contents);
//image viewer
if (!MiscUtils.isPureAscii(contentsAsString))
{
//TODO webp?
if (nameLowerCase.endsWith(".png") || nameLowerCase.endsWith(".jpg") || nameLowerCase.endsWith(".jpeg") ||
nameLowerCase.endsWith(".gif") || nameLowerCase.endsWith(".tif") || nameLowerCase.endsWith(".bmp"))
{
canRefresh = true;
image = MiscUtils.loadImage(image, contents); //gifs fail because of this
mainPanel.add(new ImageJLabel(image), BorderLayout.CENTER);
mainPanel.addMouseWheelListener(e ->
{
int notches = e.getWheelRotation();
if (notches < 0) //zoom in
image = Scalr.resize(image, Scalr.Method.SPEED, image.getWidth() + 10,
image.getHeight() + 10);
else //zoom out
image = Scalr.resize(image, Scalr.Method.SPEED, image.getWidth() - 10,
image.getHeight() - 10);
mainPanel.removeAll();
mainPanel.add(new ImageJLabel(image), BorderLayout.CENTER);
mainPanel.updateUI();
});
return;
}
//hex viewer
else if (BytecodeViewer.viewer.forcePureAsciiAsText.isSelected())
{
JHexEditor hex = new JHexEditor(contents);
mainPanel.add(hex);
return;
}
}
textArea.setCodeFoldingEnabled(true);
textArea.setSyntaxEditingStyle(SyntaxLanguage.detectLanguage(nameLowerCase, contentsAsString).getSyntaxConstant());
textArea.setText(contentsAsString);
textArea.setCaretPosition(0);
mainPanel.add(textArea.getScrollPane());
}
public void refresh(JButton src)
{
if (!canRefresh)
{
src.setEnabled(true);
return;
}
mainPanel.removeAll();
image = MiscUtils.loadImage(image, contents);
JLabel label = new JLabel("", new ImageIcon(image), JLabel.CENTER);
mainPanel.add(label, BorderLayout.CENTER);
mainPanel.updateUI();
src.setEnabled(true);
}
private static final long serialVersionUID = 6103372882168257164L;
}

View file

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui; package the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer;
import javax.swing.JPanel; import javax.swing.JPanel;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
@ -22,11 +22,11 @@ import the.bytecode.club.bytecodeviewer.util.FileContainer;
* along with this program. If not, see <http://www.gnu.org/licenses/>. * * along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/ ***************************************************************************/
public abstract class Viewer extends JPanel { public abstract class ResourceViewer extends JPanel
{
public ClassNode cn; public ClassNode cn;
public String name; public String name;
FileContainer container; public FileContainer container;
private static final long serialVersionUID = -2965538493489119191L; private static final long serialVersionUID = -2965538493489119191L;
} }

View file

@ -0,0 +1,39 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.synchronizedscroll;
import org.objectweb.asm.Type;
import java.util.Arrays;
/**
* @author Konloch
* @since 6/24/2021
*/
public class MethodData
{
public String name, desc;
@Override
public boolean equals(final Object o)
{
return equals((MethodData) o);
}
public boolean equals(final MethodData md)
{
return this.name.equals(md.name) && this.desc.equals(md.desc);
}
public String constructPattern()
{
final StringBuilder pattern = new StringBuilder();
pattern.append(name).append(" *\\(");
final org.objectweb.asm.Type[] types = org.objectweb.asm.Type
.getArgumentTypes(desc);
pattern.append("(.*)");
Arrays.stream(types).map(Type::getClassName)
.forEach(clazzName -> pattern.append(clazzName.substring(clazzName.lastIndexOf(".") + 1)).append(
"(.*)"));
pattern.append("\\) *\\{");
return pattern.toString();
}
}

View file

@ -0,0 +1,51 @@
package the.bytecode.club.bytecodeviewer.gui.theme;
import com.bulenkov.darcula.DarculaLaf;
import javax.swing.*;
/**
* @author Konloch
* @author ThexXTURBOXx
* @since 6/24/2021
*/
public enum LAFTheme
{
LIGHT("Light Theme (Requires restart)", RSTATheme.DEFAULT), //System theme
DARK("Dark Theme (Requires restart)", RSTATheme.DARK), //Darcula
;
private final String readableName;
private final RSTATheme rstaTheme;
LAFTheme(String readableName, RSTATheme rstaTheme)
{
this.readableName = readableName;
this.rstaTheme = rstaTheme;
}
public String getReadableName()
{
return readableName;
}
public RSTATheme getRSTATheme()
{
return rstaTheme;
}
public void setLAF() throws ClassNotFoundException, UnsupportedLookAndFeelException, InstantiationException, IllegalAccessException
{
switch(this)
{
default:
case LIGHT:
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
break;
case DARK:
UIManager.setLookAndFeel(new DarculaLaf());
break;
}
}
}

View file

@ -0,0 +1,54 @@
package the.bytecode.club.bytecodeviewer.gui.theme;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Theme;
import the.bytecode.club.bytecodeviewer.Constants;
/**
* @author ThexXTURBOXx
* @since 6/23/2021
*/
public enum RSTATheme
{
DEFAULT("Default (Recommended Light)", null),
DARK("Dark (Recommended Dark)", "/org/fife/ui/rsyntaxtextarea/themes/dark.xml"),
DEFAULT_ALT( "Default-Alt", "/org/fife/ui/rsyntaxtextarea/themes/default-alt.xml"),
ECLIPSE("Eclipse", "/org/fife/ui/rsyntaxtextarea/themes/eclipse.xml"),
IDEA("IntelliJ", "/org/fife/ui/rsyntaxtextarea/themes/idea.xml"),
VS("Visual Studio", "/org/fife/ui/rsyntaxtextarea/themes/vs.xml"),
DRUID( "Druid (Dark)", "/org/fife/ui/rsyntaxtextarea/themes/druid.xml"),
MONOKAI( "Monokai (Dark)", "/org/fife/ui/rsyntaxtextarea/themes/monokai.xml");
private final String readableName;
private final String file;
RSTATheme(String readableName, String file)
{
this.readableName = readableName;
this.file = file;
}
public String getReadableName() {
return readableName;
}
public RSyntaxTextArea apply(RSyntaxTextArea area) {
if (file != null) {
try {
Theme.load(Constants.class.getResourceAsStream(file)).apply(area);
} catch (Throwable ignored) {
}
}
return area;
}
public static RSTATheme parse(String name) {
for (RSTATheme t : values()) {
if (t.name().equals(name)) {
return t;
}
}
return DEFAULT;
}
}

View file

@ -1,4 +1,6 @@
package the.bytecode.club.bytecodeviewer.gui; package the.bytecode.club.bytecodeviewer.gui.util;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.TabbedPane;
import java.awt.Color; import java.awt.Color;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
@ -6,7 +8,8 @@ import javax.swing.SwingUtilities;
/** /**
* @author Konloch * @author Konloch
*/ */
public class DelayTabbedPaneThread extends Thread { public class DelayTabbedPaneThread extends Thread
{
public boolean stopped = false; public boolean stopped = false;
private final TabbedPane pane; private final TabbedPane pane;

View file

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui; package the.bytecode.club.bytecodeviewer.gui.util;
/*************************************************************************** /***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite * * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
@ -19,17 +19,13 @@ package the.bytecode.club.bytecodeviewer.gui;
***************************************************************************/ ***************************************************************************/
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Font; import java.awt.Font;
import java.awt.event.InputEvent; import java.awt.event.InputEvent;
import java.util.Objects; import java.util.Objects;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import javax.swing.JComboBox; import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JViewport; import javax.swing.JViewport;
import javax.swing.ListCellRenderer;
import javax.swing.event.CaretEvent; import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener; import javax.swing.event.CaretListener;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
@ -37,9 +33,12 @@ import javax.swing.event.ChangeListener;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rtextarea.RTextScrollPane; import org.fife.ui.rtextarea.RTextScrollPane;
import the.bytecode.club.bytecodeviewer.BytecodeViewer; import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.components.MethodsRenderer;
import the.bytecode.club.bytecodeviewer.gui.components.SearchableRSyntaxTextArea;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.util.MethodParser; import the.bytecode.club.bytecodeviewer.util.MethodParser;
import static the.bytecode.club.bytecodeviewer.gui.TabbedPane.BLANK; import static the.bytecode.club.bytecodeviewer.gui.resourceviewer.TabbedPane.BLANK_COLOR;
/** /**
* Allows us to run a background thread * Allows us to run a background thread
@ -47,21 +46,40 @@ import static the.bytecode.club.bytecodeviewer.gui.TabbedPane.BLANK;
* @author Konloch * @author Konloch
* @author DreamSworK * @author DreamSworK
*/ */
public abstract class PaneUpdaterThread extends Thread { public abstract class PaneUpdaterThread implements Runnable
{
public ClassViewer viewer; public ClassViewer viewer;
public RSyntaxTextArea panelArea; public SearchableRSyntaxTextArea updateUpdaterTextArea;
public RTextScrollPane scrollPane;
public JComboBox<Integer> methodsList; public JComboBox<Integer> methodsList;
public int decompiler; private Thread thread;
public int paneId; public int paneIndex;
public int decompilerViewIndex;
public PaneUpdaterThread(int paneIndex, int decompilerViewIndex)
{
this.paneIndex = paneIndex;
this.decompilerViewIndex = decompilerViewIndex;
}
public abstract void doShit(); public abstract void doShit();
public void startNewThread()
{
thread = new Thread(this);
thread.start();
}
@Override @Override
public void run() { public void run()
{
if(decompilerViewIndex == 0)
return;
doShit(); doShit();
synchronizePane(); synchronizePane();
attachCtrlMouseWheelZoom(scrollPane, panelArea); //freezes the UI for some reason
attachCtrlMouseWheelZoom(updateUpdaterTextArea.getScrollPane(), updateUpdaterTextArea); //freezes the UI for some reason
//probably cause BCV is doing dumb shit with the swing thread //probably cause BCV is doing dumb shit with the swing thread
} }
@ -89,9 +107,9 @@ public abstract class PaneUpdaterThread extends Thread {
public final CaretListener caretListener = new CaretListener() { public final CaretListener caretListener = new CaretListener() {
@Override @Override
public void caretUpdate(CaretEvent e) { public void caretUpdate(CaretEvent e) {
MethodParser methods = viewer.methods.get(paneId); MethodParser methods = viewer.methods.get(paneIndex);
if (methods != null) { if (methods != null) {
int methodLine = methods.findActiveMethod(panelArea.getCaretLineNumber()); int methodLine = methods.findActiveMethod(updateUpdaterTextArea.getCaretLineNumber());
if (methodLine != -1) { if (methodLine != -1) {
if (BytecodeViewer.viewer.showClassMethods.isSelected()) { if (BytecodeViewer.viewer.showClassMethods.isSelected()) {
if (methodsList != null) { if (methodsList != null) {
@ -102,11 +120,11 @@ public abstract class PaneUpdaterThread extends Thread {
} }
if (BytecodeViewer.viewer.synchronizedViewing.isSelected()) { if (BytecodeViewer.viewer.synchronizedViewing.isSelected()) {
int panes = 2; int panes = 2;
if (viewer.panel3 != null) if (viewer.resourceViewPanel3.panel != null)
panes = 3; panes = 3;
for (int i = 0; i < panes; i++) { for (int i = 0; i < panes; i++) {
if (i != paneId) { if (i != paneIndex) {
ClassViewer.selectMethod(viewer, i, methods.getMethod(methodLine)); ClassViewer.selectMethod(viewer, i, methods.getMethod(methodLine));
} }
} }
@ -120,41 +138,41 @@ public abstract class PaneUpdaterThread extends Thread {
@Override @Override
public void stateChanged(ChangeEvent e) { public void stateChanged(ChangeEvent e) {
int panes = 2; int panes = 2;
if (viewer.panel3 != null) if (viewer.resourceViewPanel3.panel != null)
panes = 3; panes = 3;
if (BytecodeViewer.viewer.synchronizedViewing.isSelected()) { if (BytecodeViewer.viewer.synchronizedViewing.isSelected()) {
if (panelArea.isShowing() && (panelArea.hasFocus() || panelArea.getMousePosition() != null)) { if (updateUpdaterTextArea.isShowing() && (updateUpdaterTextArea.hasFocus() || updateUpdaterTextArea.getMousePosition() != null)) {
int caretLine = panelArea.getCaretLineNumber(); int caretLine = updateUpdaterTextArea.getCaretLineNumber();
int maxViewLine = ClassViewer.getMaxViewLine(panelArea); int maxViewLine = ClassViewer.getMaxViewLine(updateUpdaterTextArea);
int activeViewLine = ClassViewer.getViewLine(panelArea); int activeViewLine = ClassViewer.getViewLine(updateUpdaterTextArea);
int activeLine = (activeViewLine == maxViewLine && caretLine > maxViewLine) ? caretLine : int activeLine = (activeViewLine == maxViewLine && caretLine > maxViewLine) ? caretLine :
activeViewLine; activeViewLine;
int activeLineDelta = -1; int activeLineDelta = -1;
MethodParser.Method activeMethod = null; MethodParser.Method activeMethod = null;
MethodParser activeMethods = viewer.methods.get(paneId); MethodParser activeMethods = viewer.methods.get(paneIndex);
if (activeMethods != null) { if (activeMethods != null) {
int activeMethodLine = activeMethods.findActiveMethod(activeLine); int activeMethodLine = activeMethods.findActiveMethod(activeLine);
if (activeMethodLine != -1) { if (activeMethodLine != -1) {
activeLineDelta = activeLine - activeMethodLine; activeLineDelta = activeLine - activeMethodLine;
activeMethod = activeMethods.getMethod(activeMethodLine); activeMethod = activeMethods.getMethod(activeMethodLine);
ClassViewer.selectMethod(panelArea, activeMethodLine); ClassViewer.selectMethod(updateUpdaterTextArea, activeMethodLine);
} }
} }
for (int i = 0; i < panes; i++) { for (int i = 0; i < panes; i++) {
if (i != paneId) { if (i != paneIndex) {
int setLine = -1; int setLine = -1;
RSyntaxTextArea area = null; RSyntaxTextArea area = null;
switch (i) { switch (i) {
case 0: case 0:
area = viewer.t1.panelArea; area = viewer.resourceViewPanel1.updateThread.updateUpdaterTextArea;
break; break;
case 1: case 1:
area = viewer.t2.panelArea; area = viewer.resourceViewPanel2.updateThread.updateUpdaterTextArea;
break; break;
case 2: case 2:
area = viewer.t3.panelArea; area = viewer.resourceViewPanel3.updateThread.updateUpdaterTextArea;
break; break;
} }
@ -183,30 +201,19 @@ public abstract class PaneUpdaterThread extends Thread {
} }
} }
}; };
class MethodsRenderer extends JLabel implements ListCellRenderer<Object> { public void synchronizePane()
public MethodsRenderer() { {
setOpaque(true); if(decompilerViewIndex == 5 || decompilerViewIndex < 0)
} return;
@Override JViewport viewport = updateUpdaterTextArea.getScrollPane().getViewport();
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected,
boolean cellHasFocus) {
MethodParser methods = viewer.methods.get(paneId);
MethodParser.Method method = methods.getMethod((Integer) value);
setText(method.toString());
return this;
}
}
public void synchronizePane() {
JViewport viewport = scrollPane.getViewport();
viewport.addChangeListener(viewportListener); viewport.addChangeListener(viewportListener);
panelArea.addCaretListener(caretListener); updateUpdaterTextArea.addCaretListener(caretListener);
final MethodParser methods = viewer.methods.get(paneId); final MethodParser methods = viewer.methods.get(paneIndex);
for (int i = 0; i < panelArea.getLineCount(); i++) { for (int i = 0; i < updateUpdaterTextArea.getLineCount(); i++) {
String lineText = ClassViewer.getLineText(panelArea, i); String lineText = ClassViewer.getLineText(updateUpdaterTextArea, i);
Matcher regexMatcher = MethodParser.regex.matcher(lineText); Matcher regexMatcher = MethodParser.regex.matcher(lineText);
if (regexMatcher.find()) { if (regexMatcher.find()) {
String methodName = regexMatcher.group("name"); String methodName = regexMatcher.group("name");
@ -221,21 +228,23 @@ public abstract class PaneUpdaterThread extends Thread {
for (Integer line : methods.getMethodsLines()) { for (Integer line : methods.getMethodsLines()) {
methodsList.addItem(line); methodsList.addItem(line);
} }
methodsList.setRenderer(new PaneUpdaterThread.MethodsRenderer()); methodsList.setRenderer(new MethodsRenderer(this));
methodsList.addActionListener(e -> { methodsList.addActionListener(e ->
{
int line = (int) Objects.requireNonNull(methodsList.getSelectedItem()); int line = (int) Objects.requireNonNull(methodsList.getSelectedItem());
RSyntaxTextArea area = null; RSyntaxTextArea area = null;
switch (paneId) { switch (paneIndex)
case 0: {
area = viewer.t1.panelArea; case 0:
break; area = viewer.resourceViewPanel1.updateThread.updateUpdaterTextArea;
case 1: break;
area = viewer.t2.panelArea; case 1:
break; area = viewer.resourceViewPanel2.updateThread.updateUpdaterTextArea;
case 2: break;
area = viewer.t3.panelArea; case 2:
break; area = viewer.resourceViewPanel3.updateThread.updateUpdaterTextArea;
break;
} }
if (area != null) if (area != null)
@ -243,11 +252,11 @@ public abstract class PaneUpdaterThread extends Thread {
}); });
JPanel panel = new JPanel(new BorderLayout()); JPanel panel = new JPanel(new BorderLayout());
panel.add(scrollPane.getColumnHeader().getComponent(0), BorderLayout.NORTH); panel.add(updateUpdaterTextArea.getScrollPane().getColumnHeader().getComponent(0), BorderLayout.NORTH);
panel.add(methodsList, BorderLayout.SOUTH); panel.add(methodsList, BorderLayout.SOUTH);
methodsList.setBackground(BLANK); methodsList.setBackground(BLANK_COLOR);
scrollPane.getColumnHeader().removeAll(); updateUpdaterTextArea.getScrollPane().getColumnHeader().removeAll();
scrollPane.getColumnHeader().add(panel); updateUpdaterTextArea.getScrollPane().getColumnHeader().add(panel);
} }
} }
} }

View file

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui.extras; package the.bytecode.club.bytecodeviewer.gui.util;
import java.awt.*; import java.awt.*;
import java.awt.font.FontRenderContext; import java.awt.font.FontRenderContext;

View file

@ -15,7 +15,7 @@ import org.objectweb.asm.tree.MethodNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer; import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Resources; import the.bytecode.club.bytecodeviewer.Resources;
import the.bytecode.club.bytecodeviewer.api.Plugin; import the.bytecode.club.bytecodeviewer.api.Plugin;
import the.bytecode.club.bytecodeviewer.gui.ClassViewer; import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.plugin.PluginManager; import the.bytecode.club.bytecodeviewer.plugin.PluginManager;
/*************************************************************************** /***************************************************************************

View file

@ -0,0 +1,170 @@
package the.bytecode.club.bytecodeviewer.util;
import javax.swing.*;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Document;
import javax.swing.text.Highlighter;
import java.awt.*;
/**
* This allows functionality to main the same between JTextArea and RSyntaxTextArea text panels
*
* @author Konloch
* @since 6/25/2021
*/
public class JTextAreaUtils
{
private static final DefaultHighlighter.DefaultHighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(new Color(255, 62, 150));
/**
* This was really interesting to write.
*
* @author Konloch
*/
public static void search(JTextArea textArea, String search, boolean forwardSearchDirection, boolean caseSensitiveSearch)
{
try
{
if (search.isEmpty())
{
highlight(textArea, "", caseSensitiveSearch);
return;
}
int startLine = textArea.getDocument().getDefaultRootElement()
.getElementIndex(textArea.getCaretPosition()) + 1;
int currentLine = 1;
boolean canSearch = false;
String[] test = (textArea.getText().split("\n").length >= 2
? textArea.getText().split("\n")
: textArea.getText().split("\r"));
int lastGoodLine = -1;
int firstPos = -1;
boolean found = false;
if (forwardSearchDirection)
{
for (String s : test)
{
if (!caseSensitiveSearch)
{
s = s.toLowerCase();
search = search.toLowerCase();
}
if (currentLine == startLine)
{
canSearch = true;
}
else if (s.contains(search))
{
if (canSearch)
{
textArea.setCaretPosition(textArea.getDocument()
.getDefaultRootElement()
.getElement(currentLine - 1)
.getStartOffset());
canSearch = false;
found = true;
}
if (firstPos == -1)
firstPos = currentLine;
}
currentLine++;
}
if (!found && firstPos != -1)
{
textArea.setCaretPosition(textArea.getDocument()
.getDefaultRootElement().getElement(firstPos - 1)
.getStartOffset());
}
}
else
{
canSearch = true;
for (String s : test)
{
if (!caseSensitiveSearch)
{
s = s.toLowerCase();
search = search.toLowerCase();
}
if (s.contains(search))
{
if (lastGoodLine != -1 && canSearch)
textArea.setCaretPosition(textArea.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1)
.getStartOffset());
lastGoodLine = currentLine;
if (currentLine >= startLine)
canSearch = false;
}
currentLine++;
}
if (lastGoodLine != -1
&& textArea.getDocument().getDefaultRootElement()
.getElementIndex(textArea.getCaretPosition()) + 1 == startLine)
{
textArea.setCaretPosition(textArea.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1).getStartOffset());
}
}
highlight(textArea, search, caseSensitiveSearch);
}
catch (Exception e)
{
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
public static void highlight(JTextArea textArea,String pattern, boolean caseSensitiveSearch)
{
if (pattern.isEmpty())
{
textArea.getHighlighter().removeAllHighlights();
return;
}
try
{
Highlighter highlighter = textArea.getHighlighter();
highlighter.removeAllHighlights();
Document doc = textArea.getDocument();
String text = doc.getText(0, doc.getLength());
int pos = 0;
if (!caseSensitiveSearch)
{
pattern = pattern.toLowerCase();
text = text.toLowerCase();
}
// Search for pattern
while ((pos = text.indexOf(pattern, pos)) >= 0)
{
// Create highlighter using private painter and apply around
// pattern
highlighter.addHighlight(pos, pos + pattern.length(), painter);
pos += pattern.length();
}
}
catch (Exception e)
{
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
}

View file

@ -1,13 +1,17 @@
package the.bytecode.club.bytecodeviewer.util; package the.bytecode.club.bytecodeviewer.util;
import org.apache.commons.lang3.StringUtils;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import java.io.BufferedReader; import javax.imageio.ImageIO;
import java.io.File; import java.awt.*;
import java.io.InputStream; import java.awt.image.BufferedImage;
import java.io.InputStreamReader; import java.io.*;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.List;
import static the.bytecode.club.bytecodeviewer.Constants.gson; import static the.bytecode.club.bytecodeviewer.Constants.gson;
@ -35,7 +39,9 @@ import static the.bytecode.club.bytecodeviewer.Constants.gson;
* @author Konloch * @author Konloch
*/ */
public class MiscUtils { public class MiscUtils
{
private static CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder(); // or "ISO-8859-1" for ISO Latin 1
private static final String AB = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; private static final String AB = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private static final String AN = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; private static final String AN = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
private static final Random rnd = new Random(); private static final Random rnd = new Random();
@ -192,4 +198,30 @@ public class MiscUtils {
field.setAccessible(true); field.setAccessible(true);
((Map<String, String>) field.get(env)).put(name, val); ((Map<String, String>) field.get(env)).put(name, val);
} }
public static BufferedImage loadImage(BufferedImage defaultImage, byte[] contents)
{
try {
return ImageIO.read(new ByteArrayInputStream(contents));
} catch (IOException e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
return defaultImage;
}
public static boolean isPureAscii(String v) {
return asciiEncoder.canEncode(v);
}
public static String getChildFromPath(String path)
{
if (path.contains("/"))
{
String[] pathParts = StringUtils.split(path, "/");
return pathParts[pathParts.length-1];
}
return path;
}
} }

View file

@ -5,7 +5,7 @@ import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer; import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.api.ExceptionUI; import the.bytecode.club.bytecodeviewer.api.ExceptionUI;
import the.bytecode.club.bytecodeviewer.decompilers.Decompilers; import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import javax.swing.*; import javax.swing.*;
import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileFilter;
@ -101,7 +101,7 @@ public class ResourceDecompiling
if (result == 0) { if (result == 0) {
Thread t12 = new Thread(() -> { Thread t12 = new Thread(() -> {
try { try {
Decompilers.procyon.decompileToZip(tempZip.getAbsolutePath(), Decompiler.PROCYON.getDecompiler().decompileToZip(tempZip.getAbsolutePath(),
MiscUtils.append(javaSucks, "-proycon.zip")); MiscUtils.append(javaSucks, "-proycon.zip"));
BytecodeViewer.viewer.updateBusyStatus(false); BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) { } catch (Exception e) {
@ -112,7 +112,7 @@ public class ResourceDecompiling
Thread t2 = new Thread(() -> { Thread t2 = new Thread(() -> {
try { try {
BytecodeViewer.viewer.updateBusyStatus(true); BytecodeViewer.viewer.updateBusyStatus(true);
Decompilers.cfr.decompileToZip(tempZip.getAbsolutePath(), Decompiler.CFR.getDecompiler().decompileToZip(tempZip.getAbsolutePath(),
MiscUtils.append(javaSucks, "-CFR.zip")); MiscUtils.append(javaSucks, "-CFR.zip"));
BytecodeViewer.viewer.updateBusyStatus(false); BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) { } catch (Exception e) {
@ -123,7 +123,7 @@ public class ResourceDecompiling
Thread t3 = new Thread(() -> { Thread t3 = new Thread(() -> {
try { try {
BytecodeViewer.viewer.updateBusyStatus(true); BytecodeViewer.viewer.updateBusyStatus(true);
Decompilers.fernflower.decompileToZip(tempZip.getAbsolutePath(), Decompiler.FERNFLOWER.getDecompiler().decompileToZip(tempZip.getAbsolutePath(),
MiscUtils.append(javaSucks, "-fernflower.zip")); MiscUtils.append(javaSucks, "-fernflower.zip"));
BytecodeViewer.viewer.updateBusyStatus(false); BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) { } catch (Exception e) {
@ -134,7 +134,7 @@ public class ResourceDecompiling
Thread t4 = new Thread(() -> { Thread t4 = new Thread(() -> {
try { try {
BytecodeViewer.viewer.updateBusyStatus(true); BytecodeViewer.viewer.updateBusyStatus(true);
Decompilers.krakatau.decompileToZip(tempZip.getAbsolutePath(), Decompiler.KRAKATAU.getDecompiler().decompileToZip(tempZip.getAbsolutePath(),
MiscUtils.append(javaSucks, "-kraktau.zip")); MiscUtils.append(javaSucks, "-kraktau.zip"));
BytecodeViewer.viewer.updateBusyStatus(false); BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) { } catch (Exception e) {
@ -146,7 +146,7 @@ public class ResourceDecompiling
if (result == 1) { if (result == 1) {
Thread t12 = new Thread(() -> { Thread t12 = new Thread(() -> {
try { try {
Decompilers.procyon.decompileToZip(tempZip.getAbsolutePath(), path); Decompiler.PROCYON.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), path);
BytecodeViewer.viewer.updateBusyStatus(false); BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) { } catch (Exception e) {
new ExceptionUI(e); new ExceptionUI(e);
@ -157,7 +157,7 @@ public class ResourceDecompiling
if (result == 2) { if (result == 2) {
Thread t12 = new Thread(() -> { Thread t12 = new Thread(() -> {
try { try {
Decompilers.cfr.decompileToZip(tempZip.getAbsolutePath(), path); Decompiler.CFR.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), path);
BytecodeViewer.viewer.updateBusyStatus(false); BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) { } catch (Exception e) {
new ExceptionUI(e); new ExceptionUI(e);
@ -168,7 +168,7 @@ public class ResourceDecompiling
if (result == 3) { if (result == 3) {
Thread t12 = new Thread(() -> { Thread t12 = new Thread(() -> {
try { try {
Decompilers.fernflower.decompileToZip(tempZip.getAbsolutePath(), path); Decompiler.FERNFLOWER.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), path);
BytecodeViewer.viewer.updateBusyStatus(false); BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) { } catch (Exception e) {
new ExceptionUI(e); new ExceptionUI(e);
@ -180,7 +180,7 @@ public class ResourceDecompiling
if (result == 4) { if (result == 4) {
Thread t12 = new Thread(() -> { Thread t12 = new Thread(() -> {
try { try {
Decompilers.krakatau.decompileToZip(tempZip.getAbsolutePath(), path); Decompiler.KRAKATAU.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), path);
BytecodeViewer.viewer.updateBusyStatus(false); BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) { } catch (Exception e) {
new ExceptionUI(e); new ExceptionUI(e);
@ -288,28 +288,28 @@ public class ResourceDecompiling
try { try {
DiskWriter.replaceFile(MiscUtils.append(file, "-proycon.java"), DiskWriter.replaceFile(MiscUtils.append(file, "-proycon.java"),
Decompilers.procyon.decompileClassNode(cn, cw.toByteArray()), false); Decompiler.PROCYON.getDecompiler().decompileClassNode(cn, cw.toByteArray()), false);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
try { try {
DiskWriter.replaceFile(MiscUtils.append(file, "-CFR.java"), DiskWriter.replaceFile(MiscUtils.append(file, "-CFR.java"),
Decompilers.cfr.decompileClassNode(cn, cw.toByteArray()), false); Decompiler.CFR.getDecompiler().decompileClassNode(cn, cw.toByteArray()), false);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
try { try {
DiskWriter.replaceFile(MiscUtils.append(file, "-fernflower.java"), DiskWriter.replaceFile(MiscUtils.append(file, "-fernflower.java"),
Decompilers.fernflower.decompileClassNode(cn, cw.toByteArray()), false); Decompiler.FERNFLOWER.getDecompiler().decompileClassNode(cn, cw.toByteArray()), false);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
try { try {
DiskWriter.replaceFile(MiscUtils.append(file, "-kraktau.java"), DiskWriter.replaceFile(MiscUtils.append(file, "-kraktau.java"),
Decompilers.krakatau.decompileClassNode(cn, cw.toByteArray()), false); Decompiler.KRAKATAU.getDecompiler().decompileClassNode(cn, cw.toByteArray()), false);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -337,8 +337,7 @@ public class ResourceDecompiling
} catch (InterruptedException ignored) { } catch (InterruptedException ignored) {
} }
} }
String contents = Decompilers.procyon.decompileClassNode(cn, String contents = Decompiler.PROCYON.getDecompiler().decompileClassNode(cn, cw.toByteArray());
cw.toByteArray());
DiskWriter.replaceFile(path, contents, false); DiskWriter.replaceFile(path, contents, false);
BytecodeViewer.viewer.updateBusyStatus(false); BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) { } catch (Exception e) {
@ -364,7 +363,7 @@ public class ResourceDecompiling
} catch (InterruptedException ignored) { } catch (InterruptedException ignored) {
} }
} }
String contents = Decompilers.cfr.decompileClassNode(cn, cw.toByteArray()); String contents = Decompiler.CFR.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(path, contents, false); DiskWriter.replaceFile(path, contents, false);
BytecodeViewer.viewer.updateBusyStatus(false); BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) { } catch (Exception e) {
@ -391,7 +390,7 @@ public class ResourceDecompiling
} catch (InterruptedException ignored) { } catch (InterruptedException ignored) {
} }
} }
String contents = Decompilers.fernflower.decompileClassNode(cn, String contents = Decompiler.FERNFLOWER.getDecompiler().decompileClassNode(cn,
cw.toByteArray()); cw.toByteArray());
DiskWriter.replaceFile(path, contents, false); DiskWriter.replaceFile(path, contents, false);
BytecodeViewer.viewer.updateBusyStatus(false); BytecodeViewer.viewer.updateBusyStatus(false);
@ -419,7 +418,7 @@ public class ResourceDecompiling
} }
} }
String contents = Decompilers.krakatau.decompileClassNode(cn, String contents = Decompiler.KRAKATAU.getDecompiler().decompileClassNode(cn,
cw.toByteArray()); cw.toByteArray());
DiskWriter.replaceFile(path, contents, false); DiskWriter.replaceFile(path, contents, false);
BytecodeViewer.viewer.updateBusyStatus(false); BytecodeViewer.viewer.updateBusyStatus(false);

View file

@ -1,7 +1,7 @@
package the.bytecode.club.bytecodeviewer.util; package the.bytecode.club.bytecodeviewer.util;
import the.bytecode.club.bytecodeviewer.BytecodeViewer; import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.extras.ExportJar; import the.bytecode.club.bytecodeviewer.gui.components.ExportJar;
import javax.swing.*; import javax.swing.*;
import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileFilter;

View file

@ -3,8 +3,8 @@ package the.bytecode.club.bytecodeviewer.util;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
public enum Language { public enum SyntaxLanguage
{
XML(SyntaxConstants.SYNTAX_STYLE_XML, (n, c) -> n.endsWith(".xml") XML(SyntaxConstants.SYNTAX_STYLE_XML, (n, c) -> n.endsWith(".xml")
|| c.startsWith("<?xml") || c.startsWith("<xml")), || c.startsWith("<?xml") || c.startsWith("<xml")),
PYTHON(SyntaxConstants.SYNTAX_STYLE_PYTHON, (n, c) -> n.endsWith(".py") || n.endsWith(".python")), PYTHON(SyntaxConstants.SYNTAX_STYLE_PYTHON, (n, c) -> n.endsWith(".py") || n.endsWith(".python")),
@ -42,13 +42,13 @@ public enum Language {
TYPESCRIPT(SyntaxConstants.SYNTAX_STYLE_TYPESCRIPT, (n, c) -> n.endsWith(".ts")), TYPESCRIPT(SyntaxConstants.SYNTAX_STYLE_TYPESCRIPT, (n, c) -> n.endsWith(".ts")),
NONE(SyntaxConstants.SYNTAX_STYLE_NONE, (n, c) -> false); NONE(SyntaxConstants.SYNTAX_STYLE_NONE, (n, c) -> false);
public static final Language[] VALUES = values(); public static final SyntaxLanguage[] VALUES = values();
private final BiFunction<String, String, Boolean> criteria; private final BiFunction<String, String, Boolean> criteria;
private final String syntaxConstant; private final String syntaxConstant;
Language(String syntaxConstant, BiFunction<String, String, Boolean> criteria) { SyntaxLanguage(String syntaxConstant, BiFunction<String, String, Boolean> criteria) {
this.criteria = criteria; this.criteria = criteria;
this.syntaxConstant = syntaxConstant; this.syntaxConstant = syntaxConstant;
} }
@ -61,13 +61,12 @@ public enum Language {
return syntaxConstant; return syntaxConstant;
} }
public static Language detectLanguage(String fileName, String content) { public static SyntaxLanguage detectLanguage(String fileName, String content) {
for (Language lang : VALUES) { for (SyntaxLanguage lang : VALUES) {
if (lang.isLanguage(fileName, content)) { if (lang.isLanguage(fileName, content)) {
return lang; return lang;
} }
} }
return NONE; return NONE;
} }
}
}

View file

@ -1,3 +0,0 @@
Manifest-Version: 1.0
Main-Class: the.bytecode.club.bytecodeviewer.BytecodeViewer

Binary file not shown.

Before

Width:  |  Height:  |  Size: 660 B

After

Width:  |  Height:  |  Size: 1.9 KiB

Before After
Before After